Product CMS
The Product CMS block is your store's back-office — a full product management interface embedded directly in your document. From a single block you can create and edit products, manage variants (including digital downloads and bundles/kits), upload images, publish product pages, connect Stripe for payments, tax, and shipping, run discount vouchers, review sales analytics, configure transactional email via Resend, organise a category hierarchy, and track orders.
Overview
The block renders a dark panel with five tabs across the top: Products, Categories, Orders, Vouchers, and Analytics. The Orders tab badge shows your unfulfilled-order count. Two integration buttons — Email and Stripe — live in the top-right corner alongside Export/Import (CSV) and the New product button.
Access model
Every server action in the Product CMS uses resolveProductOwnerId to determine which store to operate on. This means:
- Product owner — always has full access to their own store.
- Document members — authenticated collaborators invited to a document containing this block. They operate as the owner and see the same store.
- Unauthenticated users — server actions reject the request immediately. The block renders the compact read-only summary.
- Authenticated non-members — the resolver falls back to their own user ID, so they see their own (likely empty) store, not the owner's.
The readOnly prop on the block component is the editor's global read-only flag — it is true on any published public page, which causes the compact summary to render for all visitors. It is not an ownership check.
Adding the block
/ on any empty paragraph, type product cms, press Enter.+ on any block → scroll to Commerce → Product CMS.The block loads its product list from a shared SWR cache — the same cache used by the Product Catalog and Related Products blocks. Changes made in the CMS reflect immediately across all commerce blocks on the page without a reload.
Products tab
Product list
The left panel shows all your products as a scrollable list of cards. Each card displays the primary image, product name, category, price, and status badge. Clicking a card opens the product detail panel on the right.
Product statuses
Every product is in one of three states that control its visibility and what actions are available:
Bulk import / export (CSV)
The Export and Import buttons in the top-right corner let you move your whole catalog in and out as a CSV file — useful for bulk-editing prices in a spreadsheet, backing up your catalog, or migrating from another store.
- Export downloads every product and variant, including bundle composition and digital-file references, as a CSV.
- Import reads a CSV file and creates products from it. New products land as drafts for you to review before syncing to Stripe and publishing — nothing goes live automatically.
- Bundle composition round-trips by SKU: a re-imported bundle re-links to its component variants as long as they exist in the same catalog (their SKUs must match).
- Digital-product fields (the file reference and filename) round-trip too — re-importing a digital product keeps its download file wired up.
Creating products
Click New product in the top-right corner. A blank product is created immediately in draft status and the product detail panel opens. Start filling in the name, price, and variants — the block auto-saves changes as you type with a 600ms debounce per field.
Product detail
Clicking a product in the list opens its detail panel on the right. The panel is divided into logical sections: status, core fields, description, images, variants, and Stripe sync.
Core fields
Images & video
The image strip supports multiple images — and video clips — per product. The first one uploaded automatically becomes the primary media — the one shown in the product list, catalog, and related products blocks.
The primary image/video is marked with a blue checkmark badge in the top-right corner of its thumbnail. The blue border also distinguishes it from secondary media. A video thumbnail carries a small “Video” tag so it's distinguishable from a still image at a glance.
.mov, since HEVC-encoded QuickTime video doesn't play reliably in every browser.Variants
Variants represent purchasable options — sizes, colors, bundles, or any dimension that changes the price or SKU. Every product needs at least one variant before it can be published.
| Column | Description |
|---|---|
| Label | The variant name shown to customers — e.g. "Small / Black" or "Bundle of 3". Editable inline. |
| Price | Per-variant price in dollars. Stored in cents. If blank, the product's base price is used. Stripe sync uses this price. |
| Size | Optional size string — "S", "M", "L", "XL", "One size", etc. |
| Color | Optional color string. |
| Stock | Inventory count. Enforced at checkout — Stripe payment fails before the buyer is charged if a race with another buyer would oversell. Leave blank for unlimited stock (never tracked, never blocked). |
| SKU | Stock Keeping Unit. Optional identifier for your own inventory tracking. |
| ● | Green dot = synced to Stripe. Grey dot = not yet synced. |
| ★ | Gold star = default variant. The default variant is the one pre-selected on the product page. |
0) to start enforcing it: every sale atomically decrements stock, and checkout is blocked once it reaches zero. This applies the same way to a bundle's component variants — see “Bundles & kits” below.Digital products
Any variant can be marked as a digital product instead of a physical one — toggle it directly on the variant row, then upload the file that should be delivered on purchase (PDF, Word/Excel/PowerPoint, CSV, plain text, or a ZIP; Replace swaps the file without losing the toggle, Remove clears both).
- Checkout skips collecting a shipping address entirely for a digital variant.
- The order is marked fulfilled the instant payment clears — no packing/shipping step.
- The buyer's confirmation email includes a direct download link. The link is a signed, temporary URL that expires after 7 days — plan your file delivery around that window (there is currently no way to extend or regenerate an individual buyer's link beyond resending the confirmation email from the Orders tab).
- Digital variants ignore the Stock field entirely — always available, nothing to decrement.
Variant actions
Bundles & kits
A product can be turned into a bundle — a single purchasable item made up of other products' variants as components (e.g. a “Starter Kit” made of three separate items you also sell individually). Toggle This is a bundle in the product detail panel to reveal the bundle editor.
- A bundle sale decrements every component's own stock by its bundle quantity × the quantity purchased — components are ordinary variants, so their stock also still applies to their own individual product page if you sell them separately.
- If a bundle's components no longer have enough combined stock to fulfil an order, checkout is blocked rather than silently overselling.
- A bundle with every component removed can't be checked out — it's refused with a clear error instead of silently selling something that delivers nothing.
- Refunding a bundle order restocks every component back automatically.
Publish lifecycle
The status panel at the top of every product detail panel drives the publish lifecycle. It shows the current status, what that means, any blocking issues, and the available action buttons.
Publish validation
Before a product can be published, four checks must pass. The panel shows them as a live checklist — it updates instantly as you edit, with no server round-trip:
- The product must have a name.
- The product must have at least one variant.
- At least one variant (or the base price) must have a price set.
- At least one variant must be synced to Stripe — the green dot in the variant table must appear on at least one row.
Publish actions by status
| Current status | Available actions |
|---|---|
| Draft | Publish product — creates a public product page, sets status to Active. Disabled if validation checklist has unresolved issues. |
| Active | Unpublish — sets status to Draft. The product page still exists but becomes inaccessible. Archive — hides the product with a toast confirmation. |
| Archived | Restore to draft — sets status to Draft. Publish directly — publishes directly from archived if validation passes. |
Product page
Publishing creates a dedicated product page in your blnk document tree. The page inherits the visual style (background color, font, content width) from the document that contains the Product CMS block.
Once published, the status panel shows two links:
Currency
Currency is controlled at two levels, accessible via Currency settingsat the bottom of the status panel:
MYR, USD, JPY). Stripe uses the currency set on the variant price at sync time — if you change currency after syncing, re-sync to update Stripe's price objects; the sync tool detects a currency change on its own and re-creates the Stripe price automatically (you don't need to touch the price itself to trigger it).Zero-decimal currencies (JPY, KRW, and similar — where Stripe doesn't use cents) are handled automatically: the price field switches to whole-number-only input and the amount sent to Stripe is not multiplied by 100.
Stripe integration
Click the Stripe button in the top-right corner of the block to open the Stripe settings panel. This is where you connect your account and sync products.
Connecting Stripe
You're taken to Stripe's own hosted onboarding page (Stripe Connect's standard-account setup). If you already have a Stripe account, you can sign in and link it; otherwise Stripe walks you through creating one.
Business details, bank account, and identity verification — all handled entirely on Stripe's own page, not in blnk. Nothing here is optional on Stripe's side; skipping a step just means Stripe brings you back to it.
Once onboarding is far enough along, Stripe sends you back to your blnk page with a stripe_connected=true query parameter. The panel detects this, loads your account status, and automatically re-syncs any products that need it (see the callout below).
The panel shows your Stripe account ID and the date connected. Funds from purchases go directly to your Stripe account — blnk never holds payments.
Syncing products
After connecting Stripe, click Sync products to register your products and variant prices with Stripe. This creates Stripe Product and Price objects for each variant that has a price set.
The sync result panel shows:
- Synced N variants — new variants registered with Stripe for the first time.
- Updated N prices — existing Stripe price objects updated because the variant price changed.
- Everything is up to date — no changes needed.
- Error lines for any variants that failed to sync.
After a successful sync, the green sync dots appear on variants in the detail panel, and the X/N synced count updates in the Stripe section of the product detail.
Collecting sales tax / VAT
The Collect sales tax / VAT toggle turns on Stripe Tax on your own connected Stripe account — blnk never collects, remits, or is liable for any tax on your sales. You are the taxable party, exactly as if you'd wired up Stripe Tax yourself on a direct Stripe integration.
dashboard.stripe.com/settings/tax. Enabling it before that's done can make checkout error, or silently calculate $0 tax on every order.Flat-rate shipping
Set a single shipping fee, charged once per order, for any order containing at least one physical (non-digital) product. Leave the field blank for free shipping — checkout still shows an explicit Free shipping line item rather than omitting it, so buyers see it was considered, not forgotten.
- Digital-only orders never collect shipping, regardless of this setting.
- The fee is per order, not per item — a cart with several physical products still only charges it once.
- There is currently no per-product or per-region shipping rate — one flat fee applies store-wide.
Disconnecting
Click Disconnect Stripe account at the bottom of the connected state. Disconnecting only removes blnk's link to it — your Stripe account, its transaction history, and its balance are completely unaffected. You can reconnect the same account, or a different one, at any time.
Email integration (Resend)
Click the Email button to open the Resend settings panel. Resend sends transactional emails (order confirmations, shipping notifications) and powers your email campaign list.
Connecting Resend
Get your API key from resend.com/api-keys. The field is a password input — click the eye icon to reveal. Tab out or click away to trigger domain fetching.
The panel calls Resend's API with your key and lists your verified domains in the From email field. Unverified domains are filtered out.
Enter the name recipients will see in their inbox (e.g. “Sarawak Travellers”) and choose an email prefix (e.g. hello) combined with one of your verified domains.
The panel saves your API key, from name, and from email. The button is disabled until all fields are filled and a domain is selected.
Editing the sender
Once connected, click Edit next to the “Sending from” section to update the display name or from email address (the account's Default sender). Changes take effect on the next email send — no reconnection needed.
Multiple sender identities
Beyond the single Default sender, you can add named sender identities (e.g. orders@, support@, founder@) in the same panel, then assign one to each of three purposes: Orders & shipping, Campaigns, and Welcome emails. Any purpose left unassigned falls back to the account's Default sender automatically — deleting an identity currently assigned to a purpose reverts that purpose to Default rather than breaking sends.
In Domain Inbox, replying to or composing a new email also shows a From dropdown of your saved identities, pre-selected to whichever identity's address matches the thread you're replying to (or the account Default for a fresh conversation).
Domains
The connected state shows all domains on your Resend account with their verification status — verified or pending. Click Refresh to re-fetch domain status after adding a new domain in Resend's dashboard.
Resend requires a verified domain to send email. If no domains are verified, the panel links directly to resend.com/domains.
Categories tab
Categories organise your products and determine their URL slug hierarchy. Click the Categories tab to manage them.
Hierarchy
Categories support 2 levels: top-level parents and their direct children (subcategories). This hierarchy determines the URL slug for every product in that category:
| Category path | Product URL slug |
|---|---|
| No category | /product-name |
| Clothing | /clothing/product-name |
| Clothing → T-Shirts | /clothing/t-shirts/product-name |
| Accessories | /accessories/product-name |
Slugs are built from the category name by lowercasing and replacing spaces with hyphens. The slug preview at the top of the category panel shows live examples from your actual category names.
Category actions
Orders tab
The Orders tab shows all orders placed through your store. The tab label shows the count of unfulfilled orders as a badge so you can spot pending fulfilment at a glance.
Each order shows the customer details, line items, payment status, and fulfilment status. You can mark orders as fulfilled or refunded directly from this tab. Order stats (total revenue, total orders, paid/unfulfilled/refunded counts) appear in a summary strip at the top.
Order confirmation email status
Each order also tracks whether its confirmation email (and, once shipped, its shipping notification) actually sent. Every order row shows a badge next to a Resend button — click Resend to manually re-send that email (for example, after reconnecting Resend if it was disconnected when the order first came in). The badge tells you exactly what happened, not just whether it worked:
| Badge | |
|---|---|
| Sent | The email went out successfully. |
| Failed | It was attempted and the send itself failed — check the error shown just below the badge (e.g. a disconnected Resend account). |
| Automation off | The relevant system automation (Order confirmation / Shipping notification) is toggled off on the Automations page — nothing was attempted at all. |
| No tracking # | You marked the order Shipped without filling in a tracking number, so the shipping email was skipped — add one and resend, or use Resend once it’s set. |
| No customer email | This order has no customer email on file, so there’s nothing to send to. |
| Not sent yet | Genuinely hasn’t been attempted yet — this is rare and usually resolves itself within moments of the order landing. |
The summary strip at the top includes an Email failed stat tile showing how many orders currently have a failed confirmation or shipping email, and the Orders tab's filter dropdown includes a “Failed” option to isolate them.
Vouchers tab
Discount codes buyers can enter at checkout. A voucher applies to the whole order, not a single line item.
Each voucher row shows how many times it's been redeemed (against its max, if one is set) and its expiry date, if any. A code is rejected at checkout once it hits its redemption cap or expiry date, or while deactivated.
Analytics tab
Revenue and best-sellers for a selected time window — 7, 30, or 90 days.
Read-only mode
When a document is viewed on a public page, or by an authenticated user with no membership relationship to the store — a visitor, or in preview mode — the block renders a compact read-only summary:
- A shopping bag icon and the total product count.
- Up to 5 products listed with their primary image thumbnail, name, and status badge.
- No editing controls, no tab navigation, no integration panels.
This means the CMS block is safe to leave on any page — non-owners will never see your inventory management interface.
Setup checklist
Click Stripe → Connect your Stripe account. Required for checkout to work.
Click Email → paste your API key → select a verified domain → connect. Required for order confirmation emails.
Go to the Categories tab and build your hierarchy. Do this before creating products so you can assign categories during product creation.
Click New product → fill in name, category, base price, description, images, and at least one variant with a price. Toggle a variant as digital and upload its file if it's not a physical good, or turn a product into a bundle if it's made of other products' variants.
In the Stripe panel: turn on tax collection only after configuring Tax Settings in your own Stripe Dashboard, and set a flat shipping rate if you charge one. In the Vouchers tab, create any discount codes you want to run.
Open the Stripe panel and click Sync products. Wait for the sync result. Green dots should appear on your variants.
Open each product, verify the checklist passes, and click Publish product. Copy the public URL and test the checkout flow — including any digital-file delivery and shipping/tax you turned on.