EmDash CMS edition
Some themes are sold in two editions. The static edition is the theme as it ships: content lives
in src/config/site.ts and in Markdown, and every change is a commit and a rebuild. The EmDash CMS
edition is the same design with a content system behind it: an admin panel at /_emdash/admin
where an editor changes copy, images, pricing, projects, services, posts and menus without touching
the repository, plus click-to-edit on the live site.
It runs on Cloudflare Workers, with D1 for content and R2 for uploads. You deploy it once, hand over the login, and your client edits from then on.
Local setup
Section titled “Local setup”npm installcp .env.example .envnpx emdash secrets generatenpm run setupnpm run devpnpm and yarn work the same way: pnpm install then pnpm run setup, or yarn then yarn setup.
Whatever you pick, use run setup, not a bare pnpm setup, which is a pnpm built-in.
npx emdash secrets generateprints a key on its first line. Paste it into.envasEMDASH_ENCRYPTION_KEY=<that key>beforenpm run setup, or the first run has nothing to encrypt with.npm run setupcreates the local SQLite database, applies the schema, and seeds the theme’s demo content, so the panel opens with the same pages you saw in the preview.- The site runs on the usual dev URL. The panel is at
/_emdash/admin.
The first visit to /_emdash/admin opens a setup wizard: you create the owner account and register a
passkey. There is no password to store. Keep that passkey, or add a second one for the client before
you hand the site over.
What is editable, and where
Section titled “What is editable, and where”| Section | What lives there |
|---|---|
| Pages | Home, Studio, Contact, Work, Services, Journal, Privacy, Terms |
| Journal | Articles, with cover image, category, date and body |
| Services | Service entries, their deliverables, process steps and detail copy |
| Work | Projects, their images, metadata and long-form case-study body |
| Media | Every uploaded image, stored in R2 and reusable across entries |
| Menus | Navigation and footer links, order included |
| General settings | Site name, contact details, social links, SEO defaults, theme color |
Pages is one collection: each route is an entry, and its sections are fields inside that entry. So the homepage hero, the pricing cards and the closing call to action are edited on the Home entry rather than scattered across the panel.
Click-to-edit on the live site
Section titled “Click-to-edit on the live site”- Sign in at
/_emdash/admin. - Choose View Site in the panel.
- Flip the Edit toggle in the toolbar that appears on the site.
Editable text becomes clickable in place. Change it where it sits, and the panel saves it to the same entry you would have opened by hand. It is the fastest path for copy fixes, and the one to show a client first.
Adding a language
Section titled “Adding a language”The CMS edition follows the same i18n model as the static edition:
- Register the locale in
src/i18n/config.ts. - Restart the dev server so the new routes are generated.
- Open any entry in the panel and use Translate to write that locale’s version.
Translations are stored per entry, not as one big file, so an untranslated entry falls back to the source language instead of blanking the page. The full checklist is on Add a language.
Reseeding and schema changes
Section titled “Reseeding and schema changes”When you pull a theme update that changes the content schema, apply it without wiping the content:
npm run setup:updateWhen you want a clean slate instead - a new project from the same theme, or a local database you have edited into a corner - delete the local database and run the full setup again:
rm -rf data.db data.db-shm data.db-wal uploadsnpm run setupThat command is local only. Never point it at a production database.
Deploying to Cloudflare
Section titled “Deploying to Cloudflare”wrangler d1 create <your-db-name>wrangler r2 bucket create <your-bucket-name>Both commands print an id. Put them into wrangler.jsonc, in the d1_databases and r2_buckets
blocks, then set the production encryption key and deploy:
wrangler secret put EMDASH_ENCRYPTION_KEYnpm run deploy:cloudflareThe deploy applies the schema to the remote database and seeds it on first run. Open
https://<your-site>/_emdash/admin and complete the setup wizard again there: the local owner
account is not copied to production.
A site of this size stays inside Cloudflare’s free tier for Workers, D1 and R2. You need a Cloudflare account and the wrangler CLI, nothing else.
Known limits
Section titled “Known limits”- EmDash is on a 0.x version and the theme pins the exact version it was built against. Upgrade
deliberately, one version at a time, and run
npm run setup:updateafter. - The Bylines and Comments sections exist in the panel but the theme does not render them. Leave them empty.
- The related-projects field on a project is a multi-select over a fixed list. When you add a new project, extend that list in the admin schema, or the new project cannot be selected as related from another one.
What’s next
Section titled “What’s next”- Customize -> Add a language - the locale registration the panel builds on.
- Customize -> Brand colors & fonts - design tokens, identical in both editions.
- Deploy -> Cloudflare Pages - the static edition’s deploy, for comparison.