Migrate from Storyblok
Keep the block-based content model. Move the application and data into your own hands. Spaces, stories, reusable blocks, nested content, and locales have close counterparts in Storypane. At that conceptual level, the mapping is nearly 1:1. Your existing content gives an agent a useful starting point instead of a blank sheet.
Our migration started with a local Storypane installation, our Storyblok space ID, and a personal access token. Our agent migrated the block definitions and content. This guide turns that approach into a repeatable workflow for your space: inspect, map, import, and verify using Storypane’s existing APIs.
1. Give your agent the right access
Start with an installed local CMS and a dedicated, empty
Development space for rehearsal. Use your CMS URL, such as
http://localhost:3130; storypane.dev is the marketing website.
| Setting | What to provide |
|---|---|
| Storypane checkout | The local source repository, including AGENTS.md, contracts, SDK, and docs. |
STORYBLOK_SPACE_ID |
The numeric source space ID. |
STORYBLOK_PAT |
A personal access token with permission to read the required source resources. |
| Source region | The region containing that space; use its documented Management API base URL. |
STORYPANE_URL |
The running local CMS origin. |
STORYPANE_SPACE_ID |
The empty rehearsal space’s UUID. |
STORYPANE_MANAGEMENT_TOKEN |
A space-scoped token created in Settings → API & preview → Management API. |
Store secrets in an ignored local file, such as .env.storyblok-migration.local,
or your agent’s local secret configuration. That filename is ignored by this
repository. Have your agent load it locally; do not paste tokens into the prompt.
Use restrictive file permissions and keep exports and ID maps out of Git too.
The Storyblok PAT is a Management API credential, distinct from a Content Delivery API access token. Start with read access to the source; the importer has no reason to edit it. Follow Storyblok’s current authentication, region, pagination, and rate-limit documentation.
For the target, grant read and the write scopes actually needed for content,
schemas, assets, datasources, and settings. Schema deployment also requires the
appropriate admin capability. Leave content:publish out of the initial import
token. All token scopes still intersect the issuer’s current permissions. See the
management guide.
2. Inventory the space before importing
Ask the agent to export and count all required resources, across every page of each endpoint. Record which exports are drafts, published snapshots, or release versions, along with the export time. If editors keep changing the source, plan a final delta export before cutover.
Include:
- Component definitions, groups, field order, labels, defaults, validation, and restrictions on nested blocks.
- Stories and folders, parent relationships, slugs/full paths, start pages, sort order, translated names/slugs, and publication state.
- Locales, datasource definitions and entries, story references, and links.
- Original assets, folders, metadata, and image usage in fields and rich text.
- Custom field plugins, embedded rich-text components, external data sources, releases, workflows, redirects, webhooks, and preview/frontend integrations that need a separate decision.
Use the components API and stories API as source references. Respect the documented rate limits and retry reads with backoff after a 429. An incomplete first page of results is not a complete export.
3. Map the familiar parts
The core concepts line up closely; the persistence model and API contracts differ. Create a mapping manifest instead of treating a Storyblok JSON export as a Storypane write request.
| Storyblok | Storypane | Mapping work |
|---|---|---|
| Space | Space with its own Postgres database | Create the target explicitly; map source and target IDs. |
| Component technical name | Block definition technical name | Keep names where valid. Review root/nestable usage when choosing Content type, Nestable, or Universal. |
| Component groups | Block-library hierarchy | Map the grouping intent to parents in the library; do not confuse library parents with nested content. |
| Text, textarea, markdown, rich text | Text with the corresponding format | Map validation/defaults. Convert rich-text nodes and marks where their representations differ. |
| Number, boolean, date/time | Number, Boolean, Date/Datetime | Normalize numeric values and UTC timestamps to the target contracts. |
| Blocks field | Blocks field and ordered child block instances | Preserve field names and order. Resolve allowed block names/groups/tags to target restrictions. |
| Single/multiple options, datasources | Option/Options with the appropriate source | Preserve option values; map datasource IDs and story-backed choices. |
| Asset | Private asset plus an Asset reference | Copy the original, then map its ID and per-use alt text. |
| Story, URL, email, or asset link | Typed Link value or story-backed options | Rewrite identifiers and handle anchors/custom attributes explicitly. |
| Folder hierarchy and stories | Content folders and stories | Preserve paths where representable; explicitly map start pages and translated slugs. |
| Translated fields or folder-based language variants | Independent story locales, or an explicitly chosen folder model | Materialize each locale’s values. Review fallback and shared-field behavior. |
Storypane delivers nested block content with the familiar component and _uid
keys. For ordinary text and block nesting, this shape will look familiar:
{
"component": "page",
"_uid": "target-page-block-uuid",
"body": [
{
"component": "hero",
"_uid": "target-hero-block-uuid",
"title": "Bring your blocks with you."
}
]
}
The IDs above illustrate the delivered shape; actual target IDs are UUIDs returned
by Storypane. Keep maps for source story IDs/UUIDs, definitions, fields, blocks,
locales, folders, datasources, and assets. Do not force numeric source IDs into
UUID fields or assume _uid values survive a copy unchanged.
Review the parts that need adaptation
- Names: target block and field names use lowercase letters, numbers, and
underscores, beginning with a letter. Record any renames and update frontend
callers. Reserved fields such as
componentand_uidare not ordinary fields. - Rich text: validate every node, mark, image, and link against the target rich-text contract. Embedded components and custom marks need explicit handling.
- Multi-assets and custom fields: there is no generic field-plugin passthrough. Choose an explicit target model, such as nested asset blocks, or make a small reviewed change in your own application. Preserve the exported source values.
- Links and references: create target story records before resolving links so
cycles can be mapped. Storypane links use typed objects; asset fields use
{ assetId, alt? }. A Storyblok asset URL or link object is not that contract. - Locales: Storypane has independent locale drafts/publications. Source fallback, non-translatable fields, and folder-language conventions need a mapping; do not silently merge languages or publish fallback values as translations.
- Apps and history: do not assume editor plugins, permissions, scheduled releases, historic revisions, webhooks, or integrations transfer with content. Inventory them and plan their equivalents separately.
Use the current core validation contracts and management client as the target reference.
4. Import in dependency order
Keep the importer small and specific to your space. Use the management API/SDK, CLI, or MCP tools behind those APIs so validation, authorization, history, and notifications remain intact. Stage definitions in a schema migration, review it, and deploy it transactionally through the CMS migration API. Content migration does not require bypassing the store with raw SQL.
- Create locales, folders, and datasources; map their target IDs.
- Stage all block definitions and fields, then resolve definition dependencies, defaults, nested-block restrictions, and library parents. Review and deploy the schema migration before creating stories from it. Schema defaults that refer to content/assets may need a second migration after those dependencies exist.
- Copy original assets through the asset upload API. Map duplicate originals deliberately and replace source IDs/URLs, including images inside rich text.
- Create story shells and their locales. Record target IDs before filling fields.
- Fill scalar fields and build nested content through structural commands. Preserve child order and map the created block IDs before writing references.
- Resolve story/asset links and validate the resulting snapshots. Finish with all imported content as drafts and an exceptions report.
In Storypane, saveBlock writes a complete editable field map; preserve all fields
you intend to keep. Blocks fields are structural, not a nested JSON value to pass
to saveBlock. Read current revisions and schema versions, and persist mutation
IDs and exact command bodies for safe retries. Resource creation is not generally
idempotent: reconcile an ambiguous response before retrying a create. Keep a
durable source-to-target manifest so reruns resume rather than duplicate records.
Never repurpose importBlocks as a Storyblok upload endpoint: it imports existing
Storypane blocks between locales. See the management API guide
for commands, multipart assets, pagination, conflicts, and retry behavior.
5. Check it in the editor
Before the full run, exercise a representative sample: a deep nested page, a translated story, a story with incoming/outgoing references, an image-rich page, and a draft that differs from its publication. Check:
- Definition/field counts and names match the mapping report; nesting restrictions, defaults, field order, and required validation behave as expected.
- Folder paths, story names, slugs, locale values, block order, and option values survive the move. Every deliberate rename or fallback is listed.
- Story links point at target stories, including cyclic references. Asset fields and rich-text images use uploaded originals rather than depending on Storyblok.
- Content opens and saves in the editor. Block/asset usage is visible where expected, and required-field errors can be resolved rather than bypassed.
- Source/target counts reconcile by type and locale; missing, skipped, and transformed records are reported. Re-running the sample does not duplicate it.
Have the agent leave the export, mapping report, manifest, importer, fixture tests, and validation summary available locally. Keep private content exports out of Git; commit reusable importer code and sanitized tests only after review.
6. Preserve live content separately from drafts
Importing a story that is marked published in Storyblok does not prove its latest draft is the published content. If preserving publication state, export the actual published snapshot separately. The Storyblok Content Delivery API can retrieve the published version with an appropriate delivery token; a Management API PAT is not a substitute for that token.
After reviewing the import, load and publish the mapped source published snapshot, then apply any newer source draft as an unpublished edit. Publish only reviewed Storypane checkpoints, with explicit reference choices where required. Do not publish unfinished drafts to make a reference resolve. If published snapshots cannot be verified, keep the target in draft and report the gap.
7. Move to production and connect your frontend
For a team installation, use linked development and production spaces. Author the schema in dev, review/promote it to protected production, and import the content into production with a separate target manifest and credentials. Cloning a dev space excludes bulk content/media; promotion does not carry your rehearsal content into production.
Update delivery calls, generated types, asset handling, and live preview for the
Storypane SDK. Familiar component names can make block renderers reusable, but
review their props and integration code. A schema/content import alone does not
switch your website. Follow the SDK,
Astro, or Next.js
guide and check actual routes, redirects, media variants, preview, and locale URLs.
Reconcile a final source delta, verify the production result, and switch the frontend when ready. Keep Storyblok and its export intact until you have checked the new site and a rollback path. Revoke migration credentials when the work is finished.
Instructions checked against Storypane revision
46eb0b5 on November 6, 2026.
Agents should re-read the contracts in the checkout they are actually migrating to.