Topics

EmDash official release is live. Upgraded from 0.22 to 1.2

  • column

On September 28, 2026, EmDash 1.0 was officially released.
EmDash is an open-source CMS developed by Cloudflare, built on Astro, with its admin interface embedded directly within your Astro site, running on Cloudflare Workers + D1 + R2.
You could say it's roughly the Astro version of WordPress.

In a previous post, our internal blog "Yuru-log" was migrated from microCMS, but it had remained on version 0.22 since June, so we took this opportunity to upgrade all the way to the latest 1.2.0 in one go.

The highlights of 1.0 are improved stability and upgrade safety, sandboxed plugins, and MCP servers for agents.

To cut to the chase, the migration was completed in a day, but there were many pitfalls.
If you start thinking "1.0 should be stable," you'll run into issues not with EmDash itself but with surrounding dependencies.

Context: 0.22 → 1.2 had to be upgraded manually

Version 1.2 includes upgrade-emdash, an official upgrade CLI.
It updates dependencies and generates a site-specific UPGRADE.md, but it only supports 0.35 and later, so it couldn't be used from 0.22.

Between 0.22 and 1.2, there are 47 database migrations, with several breaking changes along the way.
It wasn't something you could just npm install emdash@latest and call it done.

1. Back up and replicate the production database locally

npx wrangler d1 export <database-name> --remote --output=backups/pre-1.2.sql

We run this through the local D1 and test migrations with the same data as production.

2. Update dependencies

Package

Before migration

After migration

emdash / @emdash-cms/cloudflare / @emdash-cms/admin

0.22.0

1.2.0

@emdash-cms/plugin-forms

0.1.1

0.2.9

astro

6.1.2

6.4.8

wrangler

4.80

4.148

wrangler needed to be bumped due to emdash 1.2's peer requirement (4.99 or higher), so I also removed the temporary workaround from the 0.22 era at this timing.
@astrojs/cloudflare met the peer requirements, so it was left as is at 13.1.6 at this point. This becomes the cause of the next trap.

  • patch-package for OAuth route: Applied to work around a 0.22 bug. Already fixed in the main body.
  • CSS injection into the admin panel: The 0.22 admin's CSS was for UI library kumo 2.4, but the lockfile was pulling 2.6, causing login and publish emphasis buttons to become invisible (transparent background with white text). This is fixed in the main body in 1.2.

3. Dev won't start (trap: miniflare version mismatch)

After bumping dependencies and running npm run dev, the startup failure was caused by miniflare version mismatch.
wrangler 4.148 uses miniflare 5 and updates the _cf_ALARM table to 3 columns in the local D1 state (under .wrangler/state/v3/).
Meanwhile,@astrojs/cloudflare 13.1.6 bundles miniflare 4, and the column count mismatch prevents it from reading the local state.

The fix is to also bump the pinned @astrojs/cloudflare to align miniflare's version with wrangler. Just remember: "align wrangler and adapter miniflare versions," and next time you'll get it right the first time.

As of version 0.22, the emdash dev command was deprecated, so the dev script was reverted to astro dev. Migration and type generation now run when astro dev starts.

4. Staging menu disappeared (gotcha: menus in seed.json)

After dev started, 90 migrations passed, and staging was pushed to the admin panel, the Japanese global navigation was empty.

The cause is the menus and widgetAreas in seed/seed.json.
EmDash's seed process deletes all items and then recreates them as empty when a menu or widget area with the same name exists.
The seed.json had menu definitions with "items": [] left as a template, which is why the menu was being reset every time the seed ran.

The fix was to delete the menus and widgetAreas keys entirely. Since
menu contents are managed in the admin panel, there's no reason to keep them in the seed.

5. English article links become /en/posts/en/slug (gotcha: locale prefix in entry.id)

This is a spec change in version 1.2. In multilingual sites,
getEmDashCollection() now returns entry.id with a locale prefix. For an English article, that's en/my-post.

Yuru Log was building article URLs from entry.id, so English article links became /en/posts/en/my-post, doubled up. We replaced all URL-building locations—
lists, pickups, search results, RSS, and canonical—with entry.data.slug. Since the type of
entry.data.slug is string | null, you also need to update the receiving type.

It helps to remember the roles: entry.id is the key, entry.data.slug is the URL, and entry.data.id is the database ULID (for API calls).

6. Errors every minute (gotcha: Cron Trigger and Worker entry)

In 1.x, the Worker's scheduled() handler drives scheduled post publishing and periodic plugin processing.
Since Cloudflare has no internal timer, without a Cron Trigger, scheduled posts won't publish at their scheduled time.
For this reason, I added a Cron Trigger to wrangler.jsonc.

"triggers": { "crons": ["* * * * *"] }

Then every minute I got "Handler does not export a scheduled() function".
The Worker entry wasn't exporting scheduled().
The fix was to replace src/worker.ts with the official entry point.

export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";

Now I have fetch and scheduled along with PluginBridge for sandbox-type plugins.

Trying it out for real

Why upgrading to 1.2 was worth it.

  • The editor has been completely redesigned and is much more intuitive. The slash menu is now grouped, and the toolbar follows your cursor. It works on mobile too.
  • Video blocks are now built-in. I had been handling video embeds with a custom plugin, so I'd like to replace that eventually.
  • All the workarounds from the 0.22 era are gone. No more patch-package or CSS injection.

Things to watch out for.

  • The pace from 1.0 to 1.2 was fast—just 10 days. We'll likely need a workflow that checks staging for each minor version release going forward.

Summary

  • 0.22 → 1.2 is a manual migration. The upgrade-emdash CLI only supports 0.35 and later.
  • Align wrangler and adapter miniflare versions → dev fails to start if they don't match
  • Don't leave empty templates for menus / widgetAreas in seed.json → they disappear with each seed
  • Multilingual sites have locale prefix in entry.id → URL comes from entry.data.slug
  • To add Cron Trigger, set Worker entry to @emdash-cms/cloudflare/worker

Each of these is just a few lines to fix once you understand the issue, but investigating one by one takes far more time than you'd expect.

That said, being able to remove all the patches and CSS workarounds we had applied in 0.22 shows that minor bugs have been steadily squashed leading up to the official release.
Six months ago it felt experimental to swap it in, but after reaching 1.0, if you factor in the maintenance effort for updates, EmDash has become a CMS worth seriously considering for production use.

About the author of this article

A "master of technique" who jumped from DTP into the web world and, before he knew it, mastered markup, frontend, direction, and accessibility. Active across multiple domains since Liberogic's early days, he's now a walking encyclopedia within the company. Recently, he's been diving deep into prompt-driven efficiency optimization, wondering "Can we rely more on AI for accessibility compliance?" Both his technology and thinking continue to evolve.

Ayumu Futamata

IAAP Certified Web Accessibility Specialist (WAS) / Markup Engineer / Frontend Engineer / Web Director

Read this staff member's article

Reliable team structure and responsive project management are our strengths

At Liberogic, our experienced staff actively drive projects forward, earning high praise from clients.
We carefully assign project managers and directors to ensure smooth project execution across all phases. We prevent unnecessary cost increases from over-commitment by deploying resources strategically, and we're known for speed in project understanding, estimation, and delivery.

* Please note that we do not actively pursue on-site SES-style staffing arrangements.

You can use virtually all major project management and chat tools, including Slack, Teams, Redmine, Backlog, Asana, Jira, Notion, Google Workspace, Zoom, Webex, and more.

Tell us about your web concerns.

Case Studies