WooCommerce-class commerce for EmDash CMS — the Astro-native, Cloudflare-powered WordPress successor.
Full-featured ecommerce in one plugin: products, cart, checkout, orders, subscriptions, multi-vendor marketplace, and more. Runs on Cloudflare Workers or Node.js (Railway, Render, etc.). Typed end-to-end. Sandbox-safe. MIT licensed.
npm create @dashcommerce@latestScaffolds a complete storefront with EmDash + DashCommerce, demo products, and Stripe test mode ready to go.
🎯 Live Demo: demo.dashcommerce.dev
📘 Docs: dashcommerce.dev/docs
💬 Issues: github.com/emdashCommerce/dashcommerce/issues
v0.2.0 on npm — compatible with EmDash 0.37+
| Package | Version | EmDash Compatibility |
|---|---|---|
@dashcommerce/core |
0.2.0 | EmDash ^0.37.0 |
@dashcommerce/create |
0.2.0 | Scaffolds EmDash 0.37+ projects |
The v1.0 feature roadmap is code-complete. SemVer: 0.x may include minor breaking changes until 1.0.0 — see CHANGELOG.md.
- Deploy Anywhere: Cloudflare Workers (edge) or Node.js (Railway, Render, your VPS)
- Modern Auth: Passkey support via EmDash's built-in auth system
- Stripe Native: Hosted Checkout, Payment Element, Subscriptions, Connect for multi-vendor
- Type-Safe: End-to-end TypeScript, from admin UI to storefront islands
- Sandbox-Safe: No Node.js built-ins — runs in EmDash's hardened plugin sandbox
- Open Source: MIT core; extensible plugin architecture
Every feature category WooCommerce ships, in one plugin:
Core Commerce: Products (simple, variable, subscription, digital), multi-currency, cart, hosted Stripe checkout, orders with refunds, customer portal
Growth Tools: Coupons, shipping zones, tax automation (Stripe Tax optional), inventory management, reviews
Advanced: Subscriptions with trials & dunning, multi-vendor marketplace (Stripe Connect), abandoned cart recovery, transactional email
Admin: React-based dashboard with 12 pages, revenue reports, top products/customers, MRR tracking
See What's in the box below for the complete feature breakdown.
DashCommerce 0.2.x requires EmDash 0.37+. If you're on 0.1.x (EmDash 0.28.x), follow the migration guide below.
Prerequisites: Backup your database and verify your local dev environment works before upgrading production.
Step 1: Update all dependencies together
# Install EmDash 0.37 + DashCommerce 0.2.x simultaneously
npm install emdash@^0.37.0 @emdash-cms/admin@^0.37.0 @dashcommerce/core@^0.2.0
# For Cloudflare deployments, also update:
npm install @emdash-cms/cloudflare@^0.37.0Step 2: Apply EmDash patch (required)
DashCommerce requires a small patch to EmDash for webhook handling and response passthrough. The patch is shipped with @dashcommerce/core@0.2.0 and documented in packages/core/patches/README.md.
# Using Bun (recommended) - add to package.json:
{
"patchedDependencies": {
"emdash@0.37.0": "node_modules/@dashcommerce/core/patches/emdash@0.37.0.patch"
}
}
# Then reinstall:
bun installSee patches/README.md for pnpm/npm/yarn instructions.
Step 3: Update Astro config (if using Cloudflare Workers)
EmDash 0.37 imports cloudflare:* runtime modules that must be externalized for Node.js builds:
// astro.config.mjs
export default defineConfig({
// ... existing config
vite: {
build: {
rollupOptions: {
external: target === "node" ? [/^cloudflare:/] : [],
},
},
},
});Step 4: Test checkout and webhooks
- Place a test order using Stripe test cards
- Verify webhook signature verification works
- Check that Stripe webhooks return HTTP 200 (not
{}) - Test subscription creation/renewal if using subscriptions
Step 5: Deploy to production
After verifying everything works locally, deploy to your hosting environment and monitor for any compatibility warnings in logs.
If you're not ready to upgrade to EmDash 0.37:
# Pin to the latest 0.1.x release
npm install @dashcommerce/core@^0.1.5
# Keep EmDash on 0.28.x
npm install emdash@^0.28.0 @emdash-cms/admin@^0.28.0The 0.1.x line remains on npm and will continue working with EmDash 0.28.x. However, new features and non-security fixes will only land in 0.2.x+.
- Minimum EmDash version: Now
0.37.0(was0.28.0) - Patch required: Must apply
emdash@0.37.0patch for webhooks to work - Node.js builds: Must externalize
cloudflare:*modules in Vite config - Runtime version check: Plugin will throw on incompatible EmDash versions (fail-closed for safety)
See CHANGELOG.md for full release notes.
| Area | What ships |
|---|---|
| Products | Simple, variable (size/color/etc), grouped, external/affiliate, subscription, digital-download — one collection, one type switch |
| Multi-currency | Per-product price maps, customer-selected currency at cart, per-currency minor-units handling |
| Cart & Checkout | Hosted Stripe Checkout (default) + embedded Payment Element fallback; Apple/Google Pay; guest + logged-in |
| Orders | Admin dashboard with refund / partial-refund UI, order timeline, draft-to-paid pipeline |
| Customers | Address book, order history, self-service portal (email-link, no password needed for first access) |
| Coupons | Fixed/percent × cart/product, free-shipping, exclusions, usage limits, per-customer caps |
| Shipping | Multi-zone, flat-rate / free / local-pickup / weight-based; per-product shipping classes |
| Tax | Flat-rate, rate-table (by country/region), or Stripe Tax (automatic); tax on shipping toggle |
| Inventory | Soft-locks during checkout prevent overselling; low-stock alerts; backorder policy per product |
| Subscriptions | Stripe Subscriptions, trials, upgrade/downgrade, pause/resume, dunning, customer portal |
| Digital downloads | Signed-URL token downloads, TTL + max-use enforcement, per-order grants |
| Reviews | Moderation queue, verified-purchase badge, review aggregates on product pages |
| Multi-vendor | Stripe Connect Express onboarding, single-vendor-per-order splits, platform fee, vendor payouts |
| Abandoned cart | Cron-driven reminder emails with signed restore links |
| Transactional email | Receipt, refund, subscription renewal, dunning, abandoned cart, review request, vendor invite/activation/payout — HTML + plain text |
| Reports | Revenue / top products / top customers / MRR inside the admin dashboard |
| Admin UI | 12 React pages + 5 dashboard widgets + 2 field widgets + 3 Portable Text blocks |
| Package | Description |
|---|---|
@dashcommerce/core |
The plugin — hooks, routes, admin UI, storefront islands |
@dashcommerce/starter |
Reference EmDash storefront that exercises every feature |
The marketing / docs site source also lives in this monorepo under site/ and serves https://dashcommerce.dev.
bun add @dashcommerce/coreRegister the plugin in astro.config.mjs:
import { dashcommerce } from "@dashcommerce/core";
emdash({ plugins: [dashcommerce()] });Merge the products collection + taxonomies into your seed file and re-apply:
bunx dashcommerce-merge-seed
bun emdash seed --on-conflict=updateOpen /_emdash/admin/plugins/dashcommerce/settings and paste your Stripe test keys.
Want sample data to play with? Add --with-demo-catalog to seed six example products (one per type) plus curated category/tag terms:
bunx dashcommerce-merge-seed --with-demo-catalog
bun emdash seed --on-conflict=updateThe merge step is idempotent — it only replaces DashCommerce's own entries (the products collection, product_category / product_tag taxonomies) and, with --with-demo-catalog, only appends demo products whose ids aren't already in your seed. Everything else is preserved. If you prefer to assemble the seed in code, import mergeDashCommerceSeed(seed, { withDemoCatalog }) from @dashcommerce/core.
Use the scaffold command from the top of this README:
npm create @dashcommerce@latestThis clones @dashcommerce/starter with a fully-wired storefront, demo catalog, and Stripe test mode ready.
Or wire it manually:
// astro.config.mjs
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { sqlite } from "emdash/db";
import { local } from "emdash/storage/local";
import { dashcommerce } from "@dashcommerce/core";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }),
plugins: [dashcommerce()],
}),
],
});bun emdash init
bunx dashcommerce-merge-seed
bun emdash seed --on-conflict=update
bun devFull walkthrough: Getting started · Stripe setup.
astro.config.mjs
└─ emdash({ plugins: [dashcommerce()] })
│
├─ Vite build ──► packages/core/src/index.ts # descriptor only, side-effect-free
│
└─ Runtime ──► packages/core/src/sandbox-entry.ts # hooks + routes
│
├─ routes/cart.ts (public)
├─ routes/checkout.ts (public)
├─ routes/webhook.ts (public; Stripe-signed)
├─ routes/admin-api.ts (admin-gated)
└─ …12 more
Hard rules the sandbox entry and everything it imports obey:
- No Node built-ins (
fs,crypto,node:*) — crypto viacrypto.subtle, HTTP viactx.http.fetch - All money as integer minor units (
Money = { currency, amount }), ISO 4217; mixed-currency ops throw - Every Stripe webhook is idempotent via unique-indexed Stripe IDs; duplicates return HTTP 200
- Webhook signature verification happens before any side effect
- Cart re-prices server-side on every mutation and on every read — no client-sent prices trusted
bun install
bun run typecheck
bun run build
bun test # 66 tests across money / cart / coupons / webhook / tokens / splitPer-package loop:
cd packages/core && bun run dev # tsdown --watch
cd packages/starter && bun run dev # Astro on :4321Stripe webhook forwarding for local dev:
stripe listen --forward-to localhost:4321/_emdash/api/plugins/dashcommerce/checkout/webhookPre-release, so the surface is still shifting. Small PRs welcome; large new features please open an issue first so we can align with the roadmap in CHANGELOG.md.
MIT