Skip to main content

Posts

A post in Flexweg CMS is a piece of dated content with a category and tags — typically a blog article, news item, case study or release note. Posts appear in listing pages (home, category archives, search results) sorted by publication date.

For pages without a date / category context (e.g. About, Contact, Privacy Policy), use Pages instead.

Posts list

Open Posts in the sidebar. You see a paginated list with columns:

  • Title — clickable, opens the editor
  • Status — Draft / Online (with a colored badge)
  • Category — primary category if set, otherwise empty
  • Author — display name resolved from users/{uid}
  • Date — most recent of (publishedAt → updatedAt → createdAt)

Above the list:

  • Search — multi-token substring match on title + slug + excerpt. Case-insensitive. Doesn't search post bodies (would be too slow client-side).
  • Filter tabs — All / Draft / Online — filters by status
  • New post button — opens the editor with an empty post

Below the list:

  • Pagination — 25 posts per page by default. Adjust in Settings → General → Performance.

Selecting one or more posts via the row checkboxes activates bulk actions:

  • Publish (re-publishes the selected drafts)
  • Unpublish (puts the selected online posts back to draft, deletes their HTML)
  • Delete (removes from Firestore + deletes their HTML)

Creating a post

Click New post. The editor opens with an empty post.

Top bar

  • Status badge (Draft / Online) — current state
  • Save button — saves the draft to Firestore without publishing
  • Publish / Update — publishes (writes the HTML to Flexweg) or re-publishes if already online
  • More menu — Unpublish, Delete

Editor area

The main editor is a block-based Tiptap-powered WYSIWYG. You write the post body here:

  • Type / hit Enter to create a paragraph
  • Press / to open the block inserter (paragraph, heading, image, list, quote, code, embed, custom HTML, columns, theme blocks)
  • Highlight text → bubble menu offers bold, italic, underline, link, etc.
  • Each top-level block has a hover toolbar (top-right of the block) with move up / move down / duplicate / delete actions

See Editor overview for details on each block type.

Two tabs: Document (post-level metadata) and Block (selected block's properties).

Document tab

  • Title — what users see (the slug auto-derives)
  • Slug — the URL segment. Auto-generated from the title; click to edit. The admin validates collisions live (see URL structure)
  • Excerpt — short summary (used by RSS, OG tags, listing pages)
  • Status — Draft (not yet published) or Online (published)
  • Publication date — defaults to "now" on first publish; editable
  • Primary category — picks a single category. Determines the post's URL: /<category-slug>/<post-slug>.html. Optional — without it, the URL is /<post-slug>.html.
  • Tags — multiple. Tags don't affect URLs but are surfaced by themes and used by flexweg-search for filtering.
  • Author — defaults to you; admins can reassign to another user
  • Hero image — picked from the media library. Themes use this for the OG image, listing card cover, and post header.
  • SEO:
    • SEO title — overrides the <title> tag (defaults to post title)
    • SEO description — overrides the <meta description> (defaults to excerpt)
    • OG image — overrides the hero for Open Graph (defaults to hero)

Block tab

Shows the inspector for the currently-selected block. Each block type has its own inspector. For example:

  • A heading has level (H1-H6)
  • An image has alt text, caption, link target, format picker
  • A custom HTML block has a CodeMirror editor for editing the HTML
  • Theme blocks have their own custom inspectors

If your cursor is on a paragraph (no inspector), the Block tab is hidden / shows "no settings".

Saving and publishing

Save draft

Click Save. The post is written to Firestore as status: "draft". No HTML is uploaded. The post appears in the list with a Draft badge.

First publish

Click Publish. The admin runs a sequence:

  1. Validates — title and slug are non-empty, slug is unique
  2. Renders the body — markdown → safe HTML via marked + DOMPurify
  3. Applies plugin filterspost.markdown.before, post.html.body, post.template.props get a chance to transform the post
  4. Renders the page — wraps the body in the active theme's BaseLayout + SingleTemplate, runs through react-dom/server.renderToStaticMarkup
  5. Hashes the resultsha256(html). If the hash matches lastPublishedHash, skips the upload (no changes since last publish)
  6. UploadsPOST /files/upload with the HTML at <category-slug>/<post-slug>.html
  7. Records — Firestore updates: status: "online", publishedAt: now, lastPublishedPath, lastPublishedHash
  8. Cascades — re-publishes the home page (because the latest-posts list changed) + every category archive that lists this post + plugin outputs (sitemaps, RSS, search index)
  9. Fires publish.complete action — plugins like flexweg-rss listen here
  10. ToastsPublished to /<category-slug>/<post-slug>.html

You see a publish log in the bottom of the editor with each step's status.

Re-publish

Once a post is online, the Publish button becomes Update. Clicking it runs the same flow — but if nothing actually changed, the upload is skipped (hash matches).

Slug change

If you change the slug or primary category, the URL changes. The admin deletes the old file before uploading the new one. If the deletion fails (transient API error), the path is recorded in previousPublishedPaths[] and retried on the next publish.

This means:

  • Your URLs stay tidy — no orphan .html files
  • Visitors with the old URL bookmarked get a 404 (the file is deleted) — consider this a feature for SEO consistency, not a bug

See Stale path cleanup.

Unpublish

More menu → Unpublish. The admin deletes the HTML file from Flexweg, clears lastPublishedPath, sets status back to draft. Listings re-render to remove the entry.

The post's content stays in Firestore — it's only the public file that's removed. Re-publish anytime.

Delete

More menu → Delete. Confirms, then:

  1. Unpublishes (deletes the HTML if currently online)
  2. Removes the Firestore document
  3. Cascades the listings update

This is destructive — there's no trash can. Make sure you mean it.

Drafts vs scheduled publishing

There's no scheduled publishing in v1. You either:

  • Save as draft (not visible publicly until you click Publish)
  • Click Publish (immediately writes the HTML)

Plugin authors could implement a scheduled queue via publish.before + setTimeout, but it'd be brittle (browser closed = no scheduled publish). For real scheduling, integrate a separate cron service that calls Firestore via the API.

Importing posts

The flexweg-import must-use plugin can bulk-create posts from:

  • A folder of markdown files (with YAML frontmatter for metadata)
  • A WordPress XML export

See [Importing content].

Continue