Strapi 5 migration guide

Migrating from Strapi 4 to Strapi 5 is not a dependency bump. The REST response format changes, numeric ids become documentId, the Entity Service you used in custom code is replaced by the Document Service, and every plugin written for v4 stops working. The official upgrade tool handles a good share of it, but the rest is manual and it is the manual part that decides whether you are done in three days or six weeks. We run Strapi 4 and Strapi 5 projects in production; this is the guide we wish we had before the first one, with the breaking changes ranked by how often they bite, the exact commands, and hour ranges for three project sizes.

Current version as of 30 September 2026: Strapi 5.56.

Why now

DateWhat happened
24 September 2024Strapi 5 released
End of October 2025Bug fixes for Strapi 4 stopped
End of April 2026Security patches for Strapi 4 stopped. 4.x is end of life

Your admin panel is a server where staff log in and upload files, and it now runs on software with no security fixes. That includes the frozen dependency tree: Koa, the React admin, upload libraries. Every new CVE stays open until you move.

Breaking changes, ranked by pain

The official list has around forty entries. These are the ones that cost hours on real projects, in the order they usually hit.

1. Flat REST responses

Strapi 4 wrapped every entry in data.attributes. Strapi 5 puts fields at the root:

json
// Strapi 4
{ "data": { "id": 14, "attributes": { "title": "Post", "cover": { "data": { "id": 3, "attributes": { "url": "/uploads/a.jpg" } } } } } }

// Strapi 5
{ "data": { "documentId": "clkgylmcc000008lcdd868feh", "title": "Post", "cover": { "documentId": "clkgylw7d000108lc4rw1bb6s", "url": "/uploads/a.jpg" } } }

Every frontend that reads .attributes breaks. The transition header Strapi-Response-Format: v4 restores the old shape per request; for GraphQL set v4CompatibilityMode: true in the plugin config. We turn compatibility on for the cutover and remove it endpoint by endpoint as the frontend is rewritten.

2. documentId replaces id

Routes like /api/articles/14 no longer work; you need /api/articles/clkgylmcc000008lcdd868feh. Any external system that stored numeric ids (a CRM, a mobile app, old deep links, a search index) needs a mapping table. Sorting by id no longer guarantees chronological order, sort by createdAt.

3. Entity Service becomes Document Service

All custom server code (controllers, services, cron jobs, lifecycle hooks) using strapi.entityService moves to strapi.documents():

js
// Strapi 4
await strapi.entityService.findOne('api::article.article', id, { populate: ['cover'] });
await strapi.entityService.findMany('api::article.article', { publicationState: 'preview' });

// Strapi 5
await strapi.documents('api::article.article').findOne({ documentId, populate: ['cover'] });
await strapi.documents('api::article.article').findMany({ status: 'draft' });

The codemod rewrites the calls, but wherever a numeric id was passed it leaves a __TODO__ marker, and every marker is a manual decision. publicationState became status; instead of writing publishedAt you call publish() and unpublish().

4. Plugins

Reasons v4 plugins fail: new Plugin SDK, @strapi/helper-plugin removed (imports move to @strapi/strapi/admin), Design System v2, Vite instead of webpack for the admin build. For every third-party plugin there are three outcomes: the author shipped a v5 version, there is a replacement, or you rewrite it. On a typical site with two to four community plugins at least one lands in the third bucket.

5. Admin customisation

Anything in src/admin (custom pages, injected components, webpack.config.js) moves to Vite: a vite.config, JSX files renamed to .jsx or .tsx, webpack aliases gone. The --bundler=webpack flag exists but prints a deprecation warning, treat it as a crutch, not a plan. Patching admin internals with patch-package no longer works because of the Vite and Rollup build.

6. Environment and database

7. Populate, Draft & Publish, i18n, reserved names

The commands

Prerequisite: be on the latest 4.x. If not:

bash
npx @strapi/upgrade minor

Then, on a copy of the project with a copy of the production database:

bash
npx @strapi/upgrade major

The tool shows the planned changes, installs Strapi 5 dependencies and runs the codemods: lifecycle files, i18n plugin removal, React and styled-components upgrades, Entity Service to Document Service, S3 credentials, sqlite3 to better-sqlite3. To see or cherry-pick codemods:

bash
npx @strapi/upgrade codemods ls
npx @strapi/upgrade codemods run

After it finishes, grep for __TODO__ and work through the list before starting the server. The first npm run develop on Strapi 5 migrates the database schema and there is no automatic way back.

Our migration plan

  1. Audit. Version, plugin list with Marketplace status, size of custom code in src/api, src/extensions, src/admin, and who consumes the API (site, app, integrations). This is the step that produces the estimate.
  2. Copy. Clone the project, load production content with strapi transfer rather than an export archive (archives with large media have failed to import for us more than once).
  3. Codemods. Run npx @strapi/upgrade major on the copy and read the diff.
  4. Manual pass. Resolve __TODO__, rewrite controllers and services that took numeric ids, fix dynamic zone populate, move admin customisation to Vite, replace or rewrite plugins.
  5. First start. Let Strapi 5 migrate the database on the copy. Keep the production backup next to it.
  6. API diff. Call every endpoint the frontend and integrations use, compare v4 and v5 responses. Decide per endpoint whether to enable Strapi-Response-Format: v4.
  7. Frontend. Replace data.attributes reads, id-based links, GraphQL queries. If another team owns the frontend they get the list of changed endpoints.
  8. Cutover. Upgrade Node on the server, build the Strapi 5 image, switch with a backup and a rollback plan (restore database plus old image). Watch logs for two weeks.

How long it takes

ProjectWhat is insideDurationWhere the time goes
Content siteTypes built in the admin, official plugins, frontend reads REST2–5 working daysCodemods, API diff, flat format on the frontend
Site with logicCustom controllers and services, dynamic zones, i18n, 2–4 community plugins1–3 weeksPlus Document Service rewrite, populate, plugin replacements
Custom adminOwn plugins, custom admin pages, webpack config, integrations keyed by id3–6 weeksPlus Vite and Plugin SDK, id mapping in external systems

For scale: one public report of a heavily customised project came in around 200 hours, roughly 120 on plugins and admin, 40 on data, 40 on the frontend. Agencies in the top search results quote "1–2 weeks, price on request"; we give a fixed price after a one-day look at the repository. Nothing in between, because custom code is different on every project.

When migrating is the wrong move

If the frontend is already on Next.js, most of the project is custom logic rather than content, and Strapi is effectively just an admin panel, price a move to Payload CMS alongside the upgrade. Payload runs inside Next.js, and for that profile the migration often costs about the same as the Strapi 5 upgrade and leaves you with one codebase and one deployment. If the site is content-heavy and editors like the Strapi admin, upgrading is cheaper and you keep the Content-Type Builder.

Get an estimate

Send us a link to the repository, or just the admin URL if the code is somewhere you cannot share yet. We check the plugins, the custom code and the API consumers and reply within a day with a fixed price and a duration. Details of the service are on strapi.digital/strapi-5-migration.

FAQ

Is Strapi 4 still supported?

No. Bug fixes for the 4.x branch stopped at the end of October 2025 and security patches at the end of April 2026. Anything found in Strapi 4 or its frozen dependencies after that stays open until you upgrade.

Can I upgrade to Strapi 5 without touching the frontend?

Temporarily. Send the header Strapi-Response-Format: v4 with REST requests and Strapi 5 wraps responses in the old data.attributes format. For GraphQL there is v4CompatibilityMode: true in the plugin config. Both are transition modes: you still have to move to the flat format and documentId, but you can do it endpoint by endpoint after the backend is live.

Do Strapi 4 plugins work in Strapi 5?

Generally no. The plugin API changed, @strapi/helper-plugin was removed, the admin now builds with Vite and Design System v2. Official plugins ship v5 versions; for third-party ones check the Marketplace, and expect at least one on a typical project to need a replacement or a rewrite.

How long does a Strapi 4 to 5 migration take?

From a few days for a content site with standard plugins to several weeks for a project with custom admin, custom plugins and integrations keyed by numeric id. One public write-up of a heavily customised project reported about 200 hours. We give a fixed estimate a day after seeing the repository.

Can I roll back after upgrading?

Not automatically. On first start Strapi 5 migrates the database schema (adds documentId, restructures drafts and locales). The only way back is a database backup plus the old code branch, which is why the upgrade is rehearsed on a copy first.

Which Node.js version does Strapi 5 need?

Active or maintenance LTS only: 22, 24 or 26. Servers on Node 18 or 20 need a Node upgrade as part of the job.

Let's discuss your project

By submitting the form you agree to the processing of your personal data to answer your request.