DOCUMENTATION

Product CMS

Last updated August 2026
Commerce Block
Pro

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, upload images, publish product pages, connect Stripe for payments, configure transactional email via Resend, organise a category hierarchy, and track orders.

The full CMS interface is accessible to the product owner and any authenticated document members (collaborators invited to the document). On a published public page — or when viewed by anyone without a membership relationship to the store — the block renders a compact read-only summary instead.

Overview

The block renders a dark panel with three tabs across the top: Products, Categories, and Orders. Two integration buttons — Email and Stripe — live in the top-right corner alongside the New product button.

Product CMS — top bar
Products
Categories
Orders
Email
Stripe
+ New product
Classic Tee
ClothingMYR 89.00
active
Denim Jacket
ClothingMYR 299.00
active
Summer Cap
AccessoriesMYR 59.00
draft
Old Logo Tee
MYR 49.00
archived

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 readOnlyprop 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

/ → Product CMS
Type / on any empty paragraph, type product cms, press Enter.
Block palette
Click + 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.

Search
Filters products by name or category name. Case-insensitive, matches substrings.
Status filter
All, Active, Draft, or Archived. Stacks with search and category filters.
Category filter
Shown when you have at least one category. Filters to products in the selected category.
Footer count
Shows the count of active, draft, and archived products across the bottom of the list.

Product statuses

Every product is in one of three states that control its visibility and what actions are available:

Three product statuses
DraftNot visible to customers
PublishedLive and purchasable
ArchivedHidden from store
Draft
The default state for a new product. Not visible to customers. You can edit all fields and upload images freely. Publish when ready.
Active
Published and live. The product has a public page, is discoverable in the catalog, and can be purchased. Unpublish or archive to hide it.
Archived
Hidden from the store. The product page still exists but is inaccessible. Restore to draft or publish directly from the archived state.

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.

Products are auto-saved as you type — there is no Save button. Changes are written to the database 600ms after you stop typing. The spinning loader in the top-right of the detail panel indicates a pending save.

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

Product name
The display name shown in the catalog, product page, and order emails.
Category
A dropdown of your categories. Selecting a category determines the product's URL slug hierarchy (e.g. /clothing/t-shirts/product-name).
Base price
The fallback price used when no variant has a price set. Stored in cents internally. Displayed in the effective currency for this product.
Description
3-row textarea for product copy. Shown on the product page and used in Stripe's product metadata.

Images

The image strip supports multiple images per product. The first image uploaded automatically becomes the primary image — the one shown in the product list, catalog, and related products blocks.

URL button
Opens an inline URL input. Type or paste an image URL and press Enter to add it.
Upload button
Opens an S3 image uploader inline. Uploads are stored in the products context with a 1:1 aspect ratio crop.
Hover → Primary
Hover any image thumbnail to reveal a "Primary" button. Clicking it marks that image as the primary and removes the flag from the previous primary.
Hover → delete
Hover any thumbnail to reveal the trash icon. Deletes the image record from the database immediately.

The primary image is marked with a blue checkmark badge in the top-right corner of its thumbnail. The blue border also distinguishes it from secondary images.

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.

Variant table — columns and states
LabelPriceSizeColorStockSKU
Small / Black$89.00SBlack24SKU
Medium / Black$89.00MBlack18SKU
Large / White$89.00LWhite6SKU
XL / White$99.00XLWhiteSKU
ColumnDescription
LabelThe variant name shown to customers — e.g. "Small / Black" or "Bundle of 3". Editable inline.
PricePer-variant price in dollars. Stored in cents. If blank, the product's base price is used. Stripe sync uses this price.
SizeOptional size string — "S", "M", "L", "XL", "One size", etc.
ColorOptional color string.
StockOptional inventory count. Informational only — not enforced at checkout.
SKUStock 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.

Variant actions

Add variant
Click Add variant in the variants header. A new blank row appears at the bottom of the table.
Edit inline
All variant fields are editable directly in the table row. Changes are auto-saved with a 600ms debounce.
★ Set default
Hover a row to reveal the star icon. Clicking it marks that variant as default and removes the flag from the previous default.
× Delete
Hover a row to reveal the × button. If you delete the default variant and others remain, the first remaining variant becomes the new default 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.
The Stripe sync requirement means you must connect Stripe and run a sync before you can publish. This ensures customers can actually check out — publishing a product with no Stripe price would result in a broken buy button.

Publish actions by status

Current statusAvailable actions
DraftPublish product — creates a public product page, sets status to Active. Disabled if validation checklist has unresolved issues.
ActiveUnpublish — sets status to Draft. The product page still exists but becomes inaccessible. Archive — hides the product with a toast confirmation.
ArchivedRestore 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:

Public URL
The customer-facing product page URL (e.g. /clothing/classic-tee). Includes a Copy button.
Editor link
Opens the product page in the blnk editor so you can customise the layout, add blocks, and style the page beyond the default product template.

Currency

Currency is controlled at two levels, accessible via Currency settingsat the bottom of the status panel:

Store default
The currency used for all products that don't have a per-product override. Changing this affects the display currency across all commerce blocks.
This product
Overrides the store default for this product only. Leave blank to use the store default. The effective currency is shown as a confirmation.
Currency is stored as a code (e.g. MYR, USD). 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.

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

1
Click "Connect your Stripe account"

You are redirected to Stripe's OAuth flow. Sign in to your existing Stripe account or create a new one.

2
Authorise the connection

Stripe redirects back to your blnk page with a stripe_connected=true query parameter. The panel detects this and loads your account status automatically.

3
Verify the connection

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.

Re-run sync any time you add new variants or change prices. Sync only updates Stripe — it does not publish or unpublish your product page.

Disconnecting

Click Disconnect Stripe account at the bottom of the connected state. Disconnecting removes the OAuth token — your Stripe account and its history are unaffected. You can reconnect 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.

If Resend is not configured, customers will not receive order confirmation emails after purchase. The status panel shows a warning banner on every product until Resend is connected.

Connecting Resend

1
Paste your Resend API key

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.

2
Domain loads automatically

The panel calls Resend's API with your key and lists your verified domains in the From email field. Unverified domains are filtered out.

3
Set your display name and from address

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.

4
Click "Connect Resend"

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 Editnext 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).

A sender identity's email must belong to one of your verified Resend domains to actually deliver — the panel shows a non-blocking amber hint if you add an identity whose domain isn't verified yet.

Domains

The connected state shows all domains on your Resend account with their verification status — verified or pending. Click Refreshto 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.

Category tree — 2-level hierarchy
Categories+ Add category
📂Clothingparent
📁T-Shirtschild
📁Hoodieschild
📂Accessoriesparent
📂Electronicsparent
Slug examples
/clothing/product-name
/clothing/t-shirts/product-name

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 pathProduct 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

Add category
Click "Add category" in the panel header to open an inline form for a new top-level category. Press Enter to save, Escape to cancel.
+ Add subcategory
Hover a top-level category row to reveal the + icon. Clicking it opens an inline form indented under that parent. Subcategories cannot have their own children.
▾ Expand / collapse
Click the chevron on a parent category to collapse its children. Expanded by default.
🌐 Publish page
Creates a public category page listing all products in that category. Opens in a new tab after creation. The page inherits the document's visual style.
Delete
Shows a toast confirmation. Deleting a parent promotes its children to top-level. Deleting a category leaves its products uncategorised — their URL slug loses the category segment.
Renaming a category is not yet supported via the UI — delete and recreate if you need to rename. Existing product page URLs that used the old slug will break, so update them from the product status panel after recreating.

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.

Orders are fetched from Stripe via your connected account. If Stripe is not connected, the Orders tab shows an empty state prompting you to connect.

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 small Sent, Failed, or Not sent yet badge next to a Resend button — click it to manually re-send that email (for example, after reconnecting Resend if it was disconnected when the order first came in).

The summary strip at the top includes an Email failedstat 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.

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

1
Connect Stripe

Click Stripe → Connect your Stripe account. Required for checkout to work.

2
Connect Resend

Click Email → paste your API key → select a verified domain → connect. Required for order confirmation emails.

3
Create your categories

Go to the Categories tab and build your hierarchy. Do this before creating products so you can assign categories during product creation.

4
Create products and variants

Click New product → fill in name, category, base price, description, images, and at least one variant with a price.

5
Sync to Stripe

Open the Stripe panel and click Sync products. Wait for the sync result. Green dots should appear on your variants.

6
Publish

Open each product, verify the checklist passes, and click Publish product. Copy the public URL and test the checkout flow.