---
title: "Migrate to Prismic"
description: "Learn how to migrate your existing content to Prismic."
category: "concepts"
audience: developers
lastUpdated: "2026-03-26T09:12:40.000Z"
---

<MigrationStudyRecruitment />

Prismic has APIs and tools to help migrate content from a different CMS to Prismic.

* [Migration API](https://prismic.io/docs/migration-api-technical-reference.md): The API for creating and updating Prismic pages.
* [Asset API](https://prismic.io/docs/asset-api-technical-reference.md): The API for creating and updating Prismic assets.
* [`@prismicio/client`](https://prismic.io/docs/technical-reference/prismicio-client.md): The core Prismic package for creating websites with Prismic.
* [`@prismicio/migrate`](https://prismic.io/docs/technical-reference/prismicio-migrate.md): A library of helpers to migrate content to Prismic.

This page guides you through these APIs and tools throughout your own migration.

> Every migration is unique because each CMS stores content differently. You will need to write code when migrating your existing content to Prismic.

Migrating from WordPress? See our [WordPress migration guide](https://prismic.io/docs/from-wordpress.md) for WordPress-specific guidance.

# Agentic migration

The [agentic migration starter](https://github.com/prismicio-community/agentic-migration-starter) is an AI-agent-powered project that automates content migration from any CMS into Prismic. It runs inside [Cursor](https://www.cursor.com/) or [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and walks you through the entire process step by step.

The starter is designed to save you significant time, but it is not a one-click solution. It works best as a collaborative workflow: the agents handle the heavy lifting (writing export scripts, analyzing content, generating converters) while you review their output and guide the process. Taking the time to review and iterate with the agent at each step will yield the best results.

> **Prerequisites:** [Node.js](https://nodejs.org/) >= 20, [Cursor](https://www.cursor.com/) or [Claude Code](https://docs.anthropic.com/en/docs/claude-code).

## Setup

Initialize the starter with the following command:

```bash
npx @slicemachine/init@latest --starter agentic-migration-starter
```

Then open the created directory in Cursor or Claude Code.

## Running the migration

Run the three agents in order. Each agent builds on the output of the previous one.

1. **Export and analyze**

   Run `/export-and-analyze`. The agent asks for your source CMS credentials and website URL, then:

   * Writes and runs TypeScript scripts to export all content from your source CMS as local JSON files.
   * Analyzes the exported content structure (types, fields, relationships, rich text format, assets).
   * Crawls your live website to map URL patterns and content hierarchy.

2. **Bootstrap models**

   Run `/bootstrap-models`. The agent reads the analysis outputs and uses the Prismic CLI to create [custom types](https://prismic.io/docs/content-modeling.md#page-types) and [slices](https://prismic.io/docs/slices.md) for your repository. You can review and iterate on the models in [Slice Machine](https://prismic.io/docs/slice-machine.md) (`npm run slicemachine`), then push them to Prismic when ready.

3. **Script migration**

   Run `/script-migration`. The agent orchestrates the full migration:

   * Plans the migration order, batching, and locale strategy.
   * Generates a migration runner and per-content-type converters.
   * Validates each converter locally before making any API calls.
   * Test-migrates a few representative documents per type for you to review.
   * Runs the full migration once you approve, with review gates between each content type.

   The agent tracks progress in a state file, so you can pause and resume across sessions.

## Tips for best results

* **Review at every step:** The review gates between phases allow for catching issues in content models or converters early, which prevents compounding problems later.
* **Iterate with the agent:** When something looks off (a field mapping, a slice structure, a rich text conversion) tell the agent and let it correct course rather than moving forward.
* **Refine models before scripting:** For large migrations, expect to spend time reviewing and adjusting content models in Slice Machine before moving to the migration scripting phase.
* **Pause and resume freely:** The agent maintains a progress file, so you can close your editor and pick up where you left off.

<MigrationStudyRecruitment heading="Trying the agentic migration? We'd love your feedback." description="Book a quick call to share your experience and help us improve the process." />

To understand each step of the migration process in detail or to migrate manually, continue with the process overview below.

# Process overview

Whether you use the agentic migration starter or migrate manually, the migration follows three stages:

1. **Prepare**: Audit your existing site and create your Prismic repository.
2. **Extract**: Download your content from your current CMS.
3. **Migrate**: Convert your content to Prismic and push it to your repository.

<VideoCardList>
  <VideoCard title="Video" description="Learn how to migrate content from your existing CMS to Prismic." href="https://www.youtube.com/watch?v=Z-ps9Tk2JfM" />
</VideoCardList>

## Prepare content

1. **Audit your existing site**

   Before migrating, take inventory of your current content and define:

   * **What to migrate**: Identify which content you'll bring to Prismic and which you can leave behind. Not everything needs to move — outdated or duplicate content can be skipped.
   * **What needs transformation**: Some content won't map directly to Prismic. Complex layouts may need to be rethought as [slices](https://prismic.io/docs/slices.md), internal links may need new URLs, and custom functionality may need to be rebuilt.

   > A thorough audit prevents surprises mid-migration and helps you design better content models.

2. **Create a Prismic repository**

   [Create a Prismic repository](https://prismic.io/dashboard/new-repository) if you don't have one yet. A Prismic repository stores a website's content and assets.

   Once created, [configure the repository's main locale and any additional locales](https://prismic.io/docs/localization.md#add-a-locale) needed for your project.

3. **Set up a project and install Slice Machine**

   Follow the instructions displayed after creating the repository. These instructions guide you through setting up a project and installing [Slice Machine](https://prismic.io/docs/slice-machine.md), Prismic's developer tool.

   > Learn more about setting up a project with Prismic using [Next.js](https://prismic.io/docs/nextjs.md), [Nuxt](https://prismic.io/docs/nuxt.md), or [SvelteKit](https://prismic.io/docs/sveltekit.md).

4. **Define content models**

   Custom type and slice models describe the shape of your content. Writers will fill in fields defined by your content models, and the [Content API](https://prismic.io/docs/fetch-content.md) will serve them to your website as JSON.

   > Defining Prismic models is comparable to working with WordPress's Advanced Custom Fields plugin or Drupal's Field API module.

   This step is necessary for the Migration API to understand the shape of each page. It also generates TypeScript types for your content models, allowing `@prismicio/client` to provide more accurate type hints.

   Once content models are defined, push them from Slice Machine to Prismic.

   [Learn how to model content](https://prismic.io/docs/content-modeling.md)

## Extract content

1. **Extract data from your current content sources**

   Extract content from your current CMS or data source before transforming it for Prismic. Common extraction methods include REST or GraphQL APIs, database exports, or reading from CSV/JSON/XML files.

   We recommend saving the extracted content to local JSON files.

   ```ts filename=example-script.ts
   import { writeFile } from "node:fs/promises";

   // Download content from a REST API
   const response = await fetch("https://api.example.com/posts");
   const posts = await response.json();

   // Save to a local JSON file
   await writeFile("extracted-posts.json", JSON.stringify(posts, null, 2));
   ```

   Working with local files makes it easier to iterate on your transformation logic, debug issues, and run multiple migration attempts without having to re-extract data every time.

   > Remember to fetch fresh data before performing the final migration.

## Migrate content

1. **Transform your content to match your Prismic models**

   Write code to convert your extracted content into [Prismic documents](#creating-documents). This involves mapping your source fields to Prismic fields and converting formats like [HTML to Prismic's rich text](#defining-rich-text-fields).

   Because content might come in a variety of forms and CMSs, we cannot provide a one-size-fits-all script that works for everyone. You will need to write your own conversion code.

   [Learn how to write a migration script](#migration-script)

2. **Upload your content to Prismic**

   Use `@prismicio/client` to migrate pages and assets in Prismic. Your content will end up in a special migration-specific [release](https://prismic.io/docs/releases.md), ready to be published.

   [Learn how to write a migration script](#migration-script)

3. **Build or update your website**

   Once your content is in Prismic, build or update your frontend to display it. See the [Next.js](https://prismic.io/docs/nextjs.md), [Nuxt](https://prismic.io/docs/nuxt.md), or [SvelteKit](https://prismic.io/docs/sveltekit.md) guide to learn how to fetch content, display slices, and set up previews.

# Write a migration script

A custom script is necessary to migrate content from your existing CMS to Prismic. This section describes how to write your own script.

<VideoCardList>
  <VideoCard title="Video" description="Learn how to write a migration script." href="https://www.youtube.com/watch?v=ddTWobwmk4c" />
</VideoCardList>

## **Script setup**

Install `@prismicio/client`, `@prismicio/migrate`, and [`dotenv`](https://www.npmjs.com/package/dotenv).

```bash
npm install @prismicio/client @prismicio/migrate dotenv
```

[Create a permanent write token](https://prismic.io/docs/custom-types-api#permanent-token-recommended) and add it to a `.env` file.

```bash filename=.env
PRISMIC_WRITE_TOKEN=YOUR_TOKEN
```

Create a `migrate.ts` script at the root of your Prismic project with the following content:

```tsx filename=migrate.ts
import "dotenv/config";
import * as prismic from "@prismicio/client";
import { htmlAsRichText } from "@prismicio/migrate";

import { repositoryName } from "./slicemachine.config.json";

// Prismic setup
const writeClient = prismic.createWriteClient(repositoryName, {
  writeToken: process.env.PRISMIC_WRITE_TOKEN,
});

const migration = prismic.createMigration();

// Custom migration code will go here...

// Execute the prepared migration at the very end of the script
await writeClient.migrate(migration, {
  reporter: (event) => console.log(event),
});
```

The following examples show how to work with the `migration` instance, before calling the [`writeClient.migrate()`](https://prismic.io/docs/technical-reference/prismicio-client?version=v7#writeclient.migrate) method. How you write your script will depend on the shape of your existing content and your Prismic content models.

> **Important**
>
> The `writeClient.migrate()` and your Prismic migration release can only handle up to 1,000 pages simultaneously. If you have more pages, we recommend batching them per locale.

## Create documents

Create a new document in the migration with [`migration.createDocument()`](https://prismic.io/docs/technical-reference/prismicio-client/v7.md#createdocument).

<VideoCardList>
  <VideoCard title="Video" description="Learn how to create pages in a migration." href="https://www.youtube.com/watch?v=_jzzxU7SxNw" />
</VideoCardList>

The document's `type`, `uid`, `lang`, and `data` properties should be provided. A `tags` property can optionally be provided. The document's title should be provided as the second argument.

```tsx
const document = migration.createDocument(
  {
    type: "page",
    // For some document types, `uid` can be optional,
    // TypeScript will let you know when it's the case.
    uid: "home",
    lang: "en-us",
    tags: ["team-marketing", "team-branding"],
    // Learn more in the "Provide document data" section.
    data: {
      /* ... */
    },
  },
  "Homepage",
);
```

### Create alternate language documents

When working on a multi-language project, you likely have documents that are translations of one another (e.g. the “À propos” page is the French translation of the English “About Us” page). When a document is a translation of a master language document, you should link them together. This allows editors to quickly switch from one translation to another and to keep track of translation progress.

To register a link between a master language document and an alternate language document, use the `masterLanguageDocument` option.

> **Important**
>
> Alternate language pages can only be linked to their master language documents when creating them. It's not possible to establish this link afterward. We strongly recommend to take the time to establish those links when planning your migration.

```tsx {23-24}
const enAboutUs = migration.createDocument(
  {
    type: "page",
    uid: "about-us",
    lang: "en-us",
    data: {
      /* ... */
    },
  },
  "About Us",
);

const frAboutUs = migration.createDocument(
  {
    type: "page",
    uid: "a-propos",
    lang: "fr-fr",
    data: {
      /* ... */
    },
  },
  "À propos",
  // Learn more in the "Define content relationships" section
  { masterLanguageDocument: enAboutUs },
);
```

### Create a document from Prismic

You can migrate content from one Prismic repository to another using [`migration.createDocumentFromPrismic()`](https://prismic.io/docs/technical-reference/prismicio-client?version=v7#migration.createdocumentfromprismic). This method takes care of updating the document’s content relationships and creating its assets.

```tsx
const otherClient = prismic.createClient("another-repository-name");

// Fetches a document from another repository
const documentFromPrismic = await otherClient.getByUID("page", "home");

const document = migration.createDocumentFromPrismic(
  documentFromPrismic,
  "Homepage",
);
```

## Update documents

You can update existing documents using [`migration.updateDocument()`](https://prismic.io/docs/technical-reference/prismicio-client?version=v7#migration.updatedocument). The document’s `uid`, `tags`, and `data` properties can be updated, as well as the title displayed in Prismic.

```tsx
const documentToUpdate = await writeClient.getByUID("page", "home");

// Make updates to the document
// E.g. add a tag
documentToUpdate.tags.push("team-marketing");
// E.g. update a document's field
documentToUpdate.data.myField = "Updated value";
// E.g. filter out slices
documentToUpdate.data.slices = documentToUpdate.data.slices.filter(
  (slice) => slice.slice_type !== "my_slice",
);

// Register the updated document without changing its title
const document = migration.updateDocument(documentToUpdate);
// Or register the updated document and change its title
const document = migration.updateDocument(documentToUpdate, "Updated title");
```

## Create assets

You can create assets using [`migration.createAsset()`](https://prismic.io/docs/technical-reference/prismicio-client/v7.md#createasset).

<VideoCardList>
  <VideoCard title="Video" description="Learn how to handle assets in a migration." href="https://www.youtube.com/watch?v=n85fri7GO6E" />
</VideoCardList>

The asset’s `file` and `filename` should be provided. Metadata about the asset can be provided, including `notes`, `credits`, `alt`, and `tags`.

```tsx
const asset = migration.createAsset(
  "https://example.com/foo.png",
  "foo.png",
  // All metadata are optional
  {
    notes: "lorem",
    credits: "ipsum",
    alt: "dolor",
    tags: ["sit", "amet"],
  },
);
```

The `file` parameter can be a URL string, a [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) object, a [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) instance, or any value accepted by `File` in your environment (e.g. a Base64 string, [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob), etc.). Any of the following will work:

```tsx
// URL-like
migration.createAsset("https://example.com/foo.png", "foo.png");
migration.createAsset(new URL("https://example.com/bar.png"), "bar.png");

// File-like
migration.createAsset(new File(["example-data"]), "baz.png");
migration.createAsset(fs.readFileSync("quux.png"), "quux.png");
```

Assets with the same source file are deduplicated. Metadata for each deduplicated asset will be merged. This becomes useful when defining asset fields.

```tsx
const fooPng = migration.createAsset("https://example.com/foo.png", "foo.png");

// Later in your code, but `foo.png` will only be created once.
const anotherFooPng = migration.createAsset(
  "https://example.com/foo.png",
  "foo.png",
);
```

## Script execution

When ready, your script can be executed with the following command:

```bash
npx tsx migration.ts
```

<VideoCardList>
  <VideoCard title="Video" description="Walk through a complete example migration script." href="https://www.youtube.com/watch?v=eLvW4Por0hI" />
</VideoCardList>

# Convert rich text

Prismic provides a helper to convert HTML content to Prismic's rich text format.

<VideoCardList>
  <VideoCard title="Video" description="Learn how to convert HTML to Prismic rich text." href="https://www.youtube.com/watch?v=HJrBl1ltuLU" />
</VideoCardList>

## Basic usage

HTML content can be serialized to Prismic rich text with [`htmlAsRichText()`](https://prismic.io/docs/technical-reference/prismicio-migrate#htmlasrichtext).

```tsx
const title = htmlAsRichText("<h1>Hello World</h1>").result;
const body = htmlAsRichText(someBlogPostHTML).result;

const document = migration.createDocument(
  {
    type: "post",
    uid: "hello-world",
    lang: "en-us",
    data: { title, body },
  },
  "Hello World",
);
```

`htmlAsRichText()` supports an optional `config` argument to configure how HTML code is converted.

[Learn more about `htmlAsRichText`](https://prismic.io/docs/technical-reference/prismicio-migrate/v0.md#htmlasrichtext)

## Images

Images in text should be registered in your migration as assets. This is done through the `serializer` config and the `migration.createAsset()` method.

```tsx {10}
const blogPostBody = htmlAsRichText(someBlogPostHTML, {
  serializer: {
    img: ({ node }) => {
      const src = node.properties.src;
      const filename = src.split("/").pop();
      const alt = node.properties.alt;

      return {
        type: "image",
        id: migration.createAsset(src, filename, { alt }),
      };
    },
  },
}).result;
```

[Learn more about migrating assets](#create-assets)

## Content relationships

Text links to other Prismic documents should be declared as content relationships. This is also done through the `serializer` config and a document reference.

```tsx {15}
const blogPostBody = htmlAsRichText(someBlogPostHTML, {
  serializer: {
    a: ({ node }) => {
      const href = node.properties.href;

      // Matches URLs like `/blog/hello-world`
      if (href.startsWith("/blog/")) {
        // e.g. `hello-world`
        const uid = href.split("/").pop();

        return {
          type: "hyperlink",
          // Creates a content relationship to
          // the blog post with a matching `uid`
          data: () => migration.getByUID("blog", uid),
        };
      }

      // Serializes other links as external links
      return "hyperlink";
    },
  },
}).result;
```

<VideoCardList>
  <VideoCard title="Video" description="Learn how to work with content relationships." href="https://www.youtube.com/watch?v=_tg-Tbq1VbQ" />
</VideoCardList>

# Assign assets

Assets can be assigned to fields using the reference returned by `migration.createAsset()`.

[Learn more about `createAsset`](#create-assets)

## Images

Image fields can be defined with their migration asset directly.

```tsx
const document = migration.createDocument(
  {
    type: "post",
    uid: "hello-world",
    lang: "en-us",
    data: {
      thumbnail: migration.createAsset(
        "https://example.com/foo.png",
        "foo.png",
      ),
    },
  },
  "Hello World",
);
```

If an image field was modeled with multiple thumbnails, it can be defined as an object with an `id` property linking to the main image, and other properties defining additional thumbnails.

```tsx
const fooPng = migration.createAsset("https://example.com/foo.png", "foo.png");

const document = migration.createDocument(
  {
    type: "post",
    uid: "hello-world",
    lang: "en-us",
    data: {
      metaImage: {
        id: fooPng,
        squared: fooPng,
        twitter: fooPng,
        openGraph: fooPng,
      },
    },
  },
  "Hello World",
);
```

## Links to media

Links to media fields can be defined as an object with an `id` property linking to the linked asset.

```tsx
const document = migration.createDocument(
  {
    type: "post",
    uid: "hello-world",
    lang: "en-us",
    data: {
      attachment: {
        link_type: "Media",
        id: migration.createAsset("https://example.com/foo.pdf", "foo.pdf"),
      },
    },
  },
  "Hello World",
);
```

# Assign content relationships

Content relationships can be assigned to fields using the reference returned by `migration.createDocument()` or `migration.updateDocument()`.

```tsx
const fooDocument = migration.createDocument(
  {
    type: "page",
    uid: "foo",
    lang: "en-us",
    data: {
      /* ... */
    },
  },
  "Foo",
);

const barDocument = migration.createDocument(
  {
    type: "page",
    uid: "bar",
    lang: "en-us",
    data: {
      related: fooDocument,
    },
  },
  "Bar",
);
```

Content relationships can also be defined with an already existing page.

```tsx
const existingBarDocument = await writeClient.getByUID("page", "bar");

const fooDocument = migration.createDocument(
  {
    type: "page",
    uid: "foo",
    lang: "en-us",
    data: {
      related: existingBarDocument,
    },
  },
  "Foo",
);
```

When facing circular content relationships (i.e. `foo` links to `bar` and `bar` to `foo`), we can define lazy content relationships with [`migration.getByUID()`](https://prismic.io/docs/technical-reference/prismicio-client?version=v7#migration.getbyuid) and [`migration.getSingle()`](https://prismic.io/docs/technical-reference/prismicio-client?version=v7#migration.getsingle).

```tsx
const settingsDocument = migration.createDocument(
  {
    type: "settings", // `settings` is a singleton page with no `uid`
    lang: "en-us",
    data: {
      related: () => migration.getByUID("page", "bar"),
    },
  },
  "Settings",
);

const barDocument = migration.createDocument(
  {
    type: "page",
    uid: "bar",
    lang: "en-us",
    data: {
      related: () => migration.getSingle("settings"),
    },
  },
  "Bar",
);
```

Lazy content relationships can be asynchronous. The following example queries for an already published page.

```tsx
const fooDocument = migration.createDocument(
  {
    type: "page",
    uid: "foo",
    lang: "en-us",
    data: {
      related: async () => await client.getByUID("page", "bar"),
    },
  },
  "Foo",
);
```
