Your first post
Get started with editing your first post, add metadata, and learn more.
Updated Aug 04, 2026
On this page
The editor is one markdown pane with a metadata sidebar. plym saves your draft to the database a second after you stop typing, and writes nothing to disk until the post is published. Publishing generates two files at the same path — HTML for browsers, markdown for agents. Unlike a database-backed CMS, a published page is a file on disk, so a content edit reaches readers only after you refresh the rendered file.
Write the post
- Press
⌘N, or open Posts and click + New post. - Type a title. The slug follows the title until you edit the slug field in the top bar; after that it stays where you put it.
- Write the body in markdown. It supports all standard markdown elements, as well as some elements built by plym. You can also hit
/key to insert a new markdown element.
Markdown syntax
plym renders markdown with Python-Markdown plus the extensions below. Code fences are highlighted in the browser by Prism, so the language tag on the fence picks the grammar.
| Syntax | Result |
|---|---|
# through ###### |
Heading. Each one gets an id and a self-link. Levels 2–4 build the table of contents. |
| Pipe-delimited rows | Table |
A fence tagged python |
Fenced code block, highlighted as Python |
~~text~~ |
Strikethrough |
- [x] item |
Task list |
[^1] and [^1]: text |
Footnote with a backlink |
 |
Image, emitted with loading="lazy" |
A fence tagged gallery |
Image grid |
The contents sidebar appears once a post has two or more ## headings.
The
galleryfence is plym's own. It emits a grid of lazy-loaded images from one markdown image per line:
```gallery


```
Preview
Press ⌘/ to render the current draft in a new tab. The preview uses the live template and CSS, so the layout matches the published page.
It renders with a placeholder byline and without tags or FAQs, which are attached at publish time. Allow pop-ups for your admin domain or the tab is blocked.
Publish
- Set Status to Published in the sidebar, or press
⌘↩. - plym renders both files and confirms with a Live. The rendered file is up to date. toast
- Click the ↗ beside the slug to view it live.
A post published without a category is served at the blog root. See Categories for serving it under a path segment instead.
Edit a published post
Autosave writes your edit to the database. It does not re-render the page, which means the content on the live page doesn't change. Press ⌘S to save and refresh the rendered file to publish your changes.
Changing the slug, the category, or the canonical URL refreshes the rendered file on its own. Changes to content, title, excerpt, cover, tags, and FAQs do not.
States
A post has one of three states.
| Status | Database | Disk | URL |
|---|---|---|---|
| Draft | Row saved and editable | No files | 404 |
| Published | published_at set |
<path>.html and <path>.md |
200 |
| Archived | Row kept, published_at cleared |
Files removed | 404 |
Archiving unpublishes a post and keeps the row, which is the reversible way to take a page down. Publishing it again stamps a new published_at; the original publication date is gone.
Deleting is separate and irreversible.
Sidebar options
| Field | Effect |
|---|---|
| Category | Moves the post to /<category>/<slug>. One category per post, or none. |
| Tags | Lowercased, deduplicated, listed at the foot of the post. Tags never change the URL. |
| Weight | Orders the blog index. |
| Cover | Renders above the title, and fills og:image plus the JSON-LD image. |
| FAQs | Renders a Frequently asked questions block and emits FAQPage JSON-LD. |
| Excerpt | Fills meta description, og:description, and the JSON-LD description. |
| Canonical URL | Replaces the generated link rel="canonical". Leave it blank unless the post is republished from elsewhere. |
| Reading time | Calculated from the body at 200 words per minute. Read-only. |
Weight is a plain integer and lower comes first. Posts with no weight sort after every weighted post, newest first. Use weight for pages that must lead the index, and leave it blank for anything chronological.
Limits
| Field | Limit |
|---|---|
| Title | 240 characters |
| Slug | 240 characters, lowercase a-z, 0-9, single hyphens between them |
| Canonical URL | 2048 characters, must start with http:// or https:// |
| FAQ question | 512 characters |
| FAQ answer | 4096 characters |
| Reserved slugs | admin, api, health, mcp, media, page, plym-admin, plym-docs, static, webfonts |
A slug is unique across posts and categories. Reusing one returns 409 posts.slug_conflict; a reserved slug returns 400 posts.reserved_slug.
Keyboard shortcuts
| Keys | Action |
|---|---|
⌘K |
Command palette |
⌘N |
New post |
⌘B |
Toggle sidebar |
? |
Shortcut help |
⌘S |
Save and refresh the rendered file |
⌘↩ |
Publish or unpublish |
⌘/ |
Open the preview in a new tab |
⌘ is Ctrl on Windows and Linux.
FAQ
My edit is saved but the live page is stale. The rendered file has not been regenerated. Press ⌘S in the editor, or click the refresh icon on the post's row. A restart does not fix it — startup only re-renders files that are missing or built by an older template.
Where does the markdown file for agents come from? The same publish step writes it from the post body, unmodified, at the same path with an .md extension.
Does a draft leak? No. GET /api/posts/{id} returns 404 for a draft unless the caller is an editor or administrator, and no file exists to serve.