Saved blocks
Save a block under a name, refer to it from anywhere, and override its arguments for one use. Saved blocks are JSON files in .deco/blocks.
The same summer card shows up on the home page and on three category pages. In code you'd extract it into a constant and import it everywhere. With Blocks, you save the call once, as .deco/blocks/SummerCard.json, and refer to it by name. Change the file and all four pages change.
This page shows what lives in the .deco folder, then how to save a block, reuse it, override its arguments for one use, and how saved blocks get edited.
The .deco folder
Before saving and reusing blocks, it helps to know where they live. Deco CMS keeps everything it reads in one folder, .deco/, in your app root (the folder with your app's package.json). Saved blocks go in .deco/blocks, next to the files that describe your code:
.deco/
├── index.ts your block map (.tsx also works)
├── blocks/ your saved blocks, one JSON file each
│ ├── HomePage.json
│ ├── SummerCard.json
│ └── SummerSEO.json
├── schema.gen.json generated by deco schema, committed
└── blocks.gen.ts generated by deco content, gitignoredThis table tells the files apart:
| File | What's in it | Who writes it | Commit it? |
|---|---|---|---|
index.ts | The block map: the functions content can call | You | Yes |
blocks/ | Saved blocks, one JSON file each | You, an AI agent or the site editor | Yes |
schema.gen.json | The editor forms, built from index.ts (see Forms from types) | deco schema | Yes |
blocks.gen.ts | The content module, built from blocks/ | deco content | No, gitignore it |
Don't edit schema.gen.json or blocks.gen.ts: the .gen. in a name marks a generated file. index.ts and blocks/ are yours. Every deco command finds this folder by walking up from where you run it, or takes --root (see Finding the .deco folder).
Save a block
A saved block is a block with a name. Each one is a JSON file in .deco/blocks, and the file name without .json is its name. Here's a product card (see Composing blocks), saved as SummerCard:
{
"__resolveType": "product-card",
"title": "Summer collection",
"product": { "__resolveType": "catalog-product", "slug": "summer-shirt" }
}What's saved is the call, not its result: catalogProduct runs each time the card is resolved, so the product stays current.
Reuse a block
Refer to a saved block by putting its name in __resolveType. { "__resolveType": "SummerCard" } means whatever is saved under SummerCard, written in place, so a home page and a campaign page can show the same card, and editing SummerCard.json updates every place that uses it:
// Saved as JSON, in HomePage.json
{
"__resolveType": "page",
"name": "Home",
"path": "/",
"sections": [{ "__resolveType": "SummerCard" }]
}From your code, pass the name to client.resolve. A string is always the name of a saved block:
const [card, error] = await client.resolve("SummerCard");In the site editor, a field lists the saved blocks that fit its type, and picking one stores this kind of reference (see Interchangeable blocks).
client.resolve(target, { run: false }) returns the JSON that would be called, with saved blocks written in place and nothing run. It's handy for previews, tooling and debugging. Code that handles this saved JSON types it as Block, { __resolveType: string; …arguments }; props never do. See client.resolve.Override arguments
A reference can add arguments. They're merged over the saved ones for that one use, and the saved block itself doesn't change:
// The call
seo({ title: "Summer sale", description: "Light layers for long days." });
// Saved as JSON
// Saved as SummerSEO
{ "__resolveType": "seo", "title": "Sunny!", "description": "Light layers for long days." }
// A reference to it, anywhere in your content
{ "__resolveType": "SummerSEO", "title": "Summer sale" }The merge is { ...saved, ...arguments }, so overrides are shallow: an override replaces a whole top-level property. The merged result is then looked up again by its __resolveType, by the lookup rule.
Editing saved blocks
Saved blocks are plain files, so you can edit them three ways:
- By hand, in your code editor.
- With an AI agent, which edits JSON with the tools it already has.
- In the site editor, which shows a form for each block and writes the file for you.
Because they're files in Git, you can commit, review, and revert content changes alongside code. The publishing workflow is a reviewed pull request merged into your production branch; your next deploy serves the merged configuration (see Publishing through Git). Local editor saves write the working tree, so review and commit them before opening the pull request.
Names
A saved block's name shares one registry with your block types (see the lookup rule), so it can't be the name of a block type, a built-in block such as page or lazy, or an alias (a second name for a block type, used when you rename one; see Rename a block type). At runtime the function wins and you get a warning; deco check makes it an error, so you catch it before you deploy.
These docs name block types in kebab-case (product-card) and saved blocks in PascalCase (SummerCard), which keeps the two apart at a glance.