v0.2.0 — a ready-to-run Astro commerce site built on EmDash CMS 0.5 and @dashcommerce/core@0.1.3. Every feature category the core plugin ships is exercised by a real page.
Live demo: demo.dashcommerce.dev · Templates: dashcommerce.dev/templates
npm create @dashcommerce@latestPrompts for a project directory + template, downloads the starter, installs, commits. Then:
cd <your-project>
bun run bootstrap # emdash init + merge-seed + seed (DB + 6 demo products)
bun run dev # Astro at :4321Open http://localhost:4321 — hero with "Enamel Mug" and a product grid. Paste your Stripe test keys at /_emdash/admin/plugins/dashcommerce/settings and you're exercising a real checkout in under a minute.
Prefer to clone directly? Works too:
git clone https://github.com/emdashCommerce/starter
cd starter && bun install && bun run bootstrap && bun run devStorefront routes
| Path | Purpose |
|---|---|
/ |
Homepage with hero, featured products, blog teaser, value props |
/shop |
Full catalog grid with currency switcher |
/shop/[slug] |
Product detail: variant picker, price map, reviews, add-to-cart |
/products/[slug] |
Alias that resolves to /shop/[slug] |
/category/[slug] |
Products filtered by taxonomy |
/tag/[slug] |
Products filtered by tag |
/cart |
Full cart page with qty / coupon / shipping controls |
/checkout |
Address → Stripe Checkout (hosted) OR inline Payment Element (embedded) |
/thank-you/[draftId] |
Post-checkout polling screen — waits for checkout.session.completed |
/account |
Customer account landing + Stripe customer-portal link (email lookup) |
/orders/lookup |
Guest-order lookup by email + order number |
/subscriptions/[token] |
Self-service subscription management (pause, resume, cancel) |
/blog, /blog/[slug], /blog/category/[slug] |
Blog (supports DashCommerce Portable Text blocks inline) |
Admin
Mounts alongside at /_emdash/admin with the full EmDash surface plus DashCommerce pages: Orders, Customers, Coupons, Shipping, Tax, Subscriptions, Reviews, Vendors, Menus, Reports, Settings — and the five dashboard widgets (Revenue, Low Stock, Recent Orders, Pending Reviews, Failed Renewals).
- Grab test keys from dashboard.stripe.com/test/apikeys.
- Open
http://localhost:4321/_emdash/admin/plugins/dashcommerce/settings. - Paste
stripeSecretKey(sk_test_…) andstripePublishableKey(pk_test_…), click Save all. - In a second terminal, forward webhook events to your dev server:
Copy the
stripe listen --forward-to localhost:4321/_emdash/api/plugins/dashcommerce/checkout/webhook
whsec_…the CLI prints → paste into Settings →stripeWebhookSecret→ save.
- Open any product from
/shop, add to cart → drawer cart → Checkout. - Fill contact form → Continue to payment.
- Stripe test card:
4242 4242 4242 4242, any future expiry, any CVC. - Redirect lands on
/thank-you/…. The page polls/orders/by-draft?id=…every 800ms until the webhook fires. - Check
/_emdash/admin/plugins/dashcommerce/orders— order is listed with a green Paid badge. - Receipt email fires (check terminal for the console transport, or an inbox if SMTP is wired).
Open the order in admin → Refund → full or partial → confirm. Order flips to Refunded / Partially refunded and a refund email is sent.
Add SUB-001 (Monthly Box) to cart → checkout. Stripe creates a Subscription with a 7-day trial. The /subscriptions/[token] page gives the customer self-service controls. invoice.payment_succeeded on cycle invoices triggers the renewal email; invoice.payment_failed starts the dunning flow.
Six products spanning every DashCommerce type:
| SKU | Title | Type | Notes |
|---|---|---|---|
MUG-001 |
Enamel Mug | simple | Physical, priced in USD/EUR/GBP |
TEE-001 |
Logo Tee | variable | Size + color variants |
BUNDLE-001 |
Starter Bundle | grouped | Bundles MUG-001 + TEE-001 |
EXT-001 |
Partner Good | external | Affiliate link, no cart action |
SUB-001 |
Monthly Box | subscription | $29/mo, 7-day trial |
DIG-001 |
Design Templates | simple + downloadable | Signed-URL token delivery |
Rebuild the seed from its TypeScript source with bun .emdash/build-seed.ts.
When you're ready to ship, the starter builds for three targets from one codebase. astro.config.mjs branches on env vars. Expect to do some post-click configuration on the hosted options — these buttons get you into the provider's dashboard with sensible defaults, not an instant production site.
Runs on @astrojs/cloudflare + @emdash-cms/cloudflare. The starter stays multi-tenant — you bring your own D1/KV/R2 ids and pass them in as Worker environment variables at deploy time. A build-time patch script wires them into the adapter-generated config right before wrangler deploy.
One-time resource provisioning:
wrangler d1 create dashcommerce-demo # copy the database_id from output
wrangler r2 bucket create dashcommerce-demo-media
wrangler kv namespace create SESSION # copy the namespace id from output
openssl rand -hex 32 | wrangler secret put EMDASH_AUTH_SECRET
openssl rand -hex 32 | wrangler secret put EMDASH_PREVIEW_SECRETSet these env vars on the Worker project (Cloudflare dashboard → Settings → Variables):
| Variable | From |
|---|---|
CF_D1_DATABASE_ID |
wrangler d1 create output |
CF_KV_SESSION_ID |
wrangler kv namespace create output |
CF_R2_BUCKET |
optional — overrides dashcommerce-demo-media |
CF_R2_PUBLIC_URL |
optional — public bucket URL for media |
Seed the D1 database (from your laptop, one time):
bun run cf:d1:seed # dumps local SQLite → applies to D1Deploy — either click the button or run locally:
CF_D1_DATABASE_ID=… CF_KV_SESSION_ID=… bun run cf:deployOn CF's hosted build, the dashboard's deploy command should be:
npx wrangler deploy --config dist/server/wrangler.json
D1 migrations must run via wrangler before deploy (no runtime DDL on Workers).
Runs on @astrojs/node + any Postgres (Neon free tier works) + S3-compat storage (R2 or AWS). Env vars on the service:
DATABASE_URL=postgres://…
SITE_URL=https://your-domain
S3_BUCKET=… S3_ENDPOINT=… S3_ACCESS_KEY_ID=… S3_SECRET_ACCESS_KEY=…
S3_REGION=auto S3_PUBLIC_URL=https://pub-…
One-time seed against the remote DB:
railway run bun run bootstrapRailway's filesystem is ephemeral — use Postgres, or mount a volume at /data and set SQLITE_URL=file:/data/data.db.
Local run with docker compose up from the repo root — storefront at localhost:4321, SQLite + uploads on named volumes:
docker compose up
docker compose exec app bun run bootstrapThe same image deploys anywhere (Fly, Render, ECS, Kubernetes, bare metal):
docker build -t ghcr.io/you/dashcommerce .
docker run -p 4321:4321 \
-e SITE_URL=https://your-domain \
-e DATABASE_URL=postgres://… # optional; defaults to SQLite in /data
-v dashcommerce_data:/data \
-v dashcommerce_uploads:/app/packages/starter/uploads \
ghcr.io/you/dashcommerceSwap SQLite for Postgres by uncommenting the db service in docker-compose.yml and setting DATABASE_URL.
This is a starting point, not a framework. Everything is standard Astro — edit freely.
- Layout + brand:
src/layouts/Shop.astro,src/components/Header.astro,src/styles/global.css - Homepage sections: admin under Pages → Home (hero, featured grid, blog teaser, value props)
- Nav / footer: admin under DashCommerce → Menus (nested up to 4 levels, with mega-menu columns)
- Product tile + detail: the starter copies components out of
@dashcommerce/core/astro/components/*; edit the local copies freely, or drop in your own
The plugin itself is @dashcommerce/core on npm. Don't fork it — customize at the site level and file an issue if the core needs to change.
MIT, same as DashCommerce.