✨ Join us July 23rd, 4:00 PM (CET) for our live session: How to Build a Winning Content Strategy for AI Search
Developer Workflow
·5 min read

How to Change Content Models Without Losing Content on a Live Prismic Site

The content models you design at launch rarely stay the same as your website matures. Designs evolve, and your content team asks for things the original models can't do. By then, the pages are full of published content.

In Prismic, content is tied to fields, so renaming a field or changing its type risks losing that content. Luckily, this is nothing to worry about with the right techniques.

This guide shows how to apply content model changes you are most likely to encounter.

Working with an AI agent? Point it here.

Give your coding agent the link to this post, and it can handle these changes for you. Tools like Claude, Codex, and Cursor follow the techniques below to move your content without dropping any of it.

Tools we'll be using

We'll use the Prismic CLI to make model changes throughout this guide. If you prefer a visual interface, you can use your repository's Type Builder. If you don't have access to the Type Builder in your repository, Slice Machine works too.

Change a field's name

Sometimes you need to change a field's name. For example, a Title field might need a clearer name, like Page Title. To do this, change the field's label but keep its ID. Content is locked to the ID, so changing it means losing the content.

Here's how to do this with the Prismic CLI. Note that the field's ID (title) doesn't change.

# Change the Title field's label
npx prismic field edit title --from-type page --label "Page Title"

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

No changes are needed in the website's code since the field ID doesn't change.

Change a field's type

Sometimes you need to change a field's type. For example, a plain text field might need to be converted to a rich text field to support formatting. You can't change a field's type in place, since that would lose the field's content.

Instead, add a new field, move the content into it, switch your code to it, then remove the old field. The old field stays in place the whole time, so your live site keeps working.

Best done with AI

This kind of field change, and every later technique that uses the Migration API, is best done with AI tools like Claude, Codex, and Cursor. Content changes can be complex, and AI tools handle them well.

In this example, we'll change a Description field from plain text to rich text that accepts basic formatting.

1. Add the new field

First, add the new field next to the old one. Give it a new ID, like description_v2. The old description field stays untouched.

# Add the new rich text field alongside the old one
npx prismic field add rich-text description_v2 --to-type page --label "Description" --allow paragraph,strong,em,hyperlink

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

2. Move the content over

Next, port the existing content into the new field with a migration script. You'll need a Prismic authentication token to use the Migration API. You can create one like this:

npx prismic token create --write

Save the token in a .env file like this:

PRISMIC_WRITE_TOKEN=your_write_token

Now write the migration script in a migrate.js file:

// migrate.js

import { createWriteClient, createMigration, isFilled } from "@prismicio/client";
import prismicConfig from "./prismic.config.json" with { type: "json" };

// Load .env into process.env (native, Node 20.12+)
process.loadEnvFile();

const writeClient = createWriteClient(prismicConfig.repositoryName, {
  writeToken: process.env.PRISMIC_WRITE_TOKEN,
});

const migration = createMigration();

// Fetch every page and copy the plain text into the new rich text field
const pages = await writeClient.getAllByType("page");

for (const page of pages) {
  if (!isFilled.keyText(page.data.description)) continue;

  page.data.description_v2 = [
    { type: "paragraph", text: page.data.description, spans: [] },
  ];

  migration.updateDocument(page);
}

await writeClient.migrate(migration, {
  reporter: (event) => console.log(event),
});

Run it with Node:

node migrate.js

This adds the content to a Migration Release as drafts. Open the release in your Prismic repository and review the changes. Make sure the new field contains the content you expect. If it looks good, publish the release.

3. Switch your code to the new field

Your pages now have both fields. Update your code to read the new one. Here we swap description for description_v2 and render it with the <PrismicRichText> component:

+ import { PrismicRichText } from "@prismicio/react";

- <p>{page.data.description}</p>
+ <PrismicRichText field={page.data.description_v2} />

Merge your code changes into production.

4. Remove the old field

Finally, once nothing uses the old field, remove it with the Prismic CLI:

# Remove the old plain text field
npx prismic field remove description --from-type page

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Split a field into two

Sometimes you need to split one field into two. For example, a single Name field might need to become separate First Name and Last Name fields so each can be used on its own.

1. Add the new fields

Add two text fields next to the old one:

# Add the two new text fields alongside the old one
npx prismic field add text first_name --to-type page
npx prismic field add text last_name --to-type page

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

2. Move the content over

Split each page's name into the two new fields. You'll need a Prismic authentication token to use the Migration API. You can create one like this:

npx prismic token create --write

Save the token in a .env file like this:

PRISMIC_WRITE_TOKEN=your_write_token

Now write the migration script in a migrate.js file:

// migrate.js

import { createWriteClient, createMigration, isFilled } from "@prismicio/client";
import prismicConfig from "./prismic.config.json" with { type: "json" };

// Load .env into process.env (native, Node 20.12+)
process.loadEnvFile();

const writeClient = createWriteClient(prismicConfig.repositoryName, {
  writeToken: process.env.PRISMIC_WRITE_TOKEN,
});

const migration = createMigration();

// Fetch every page and split the name into the two new fields
const pages = await writeClient.getAllByType("page");

for (const page of pages) {
  if (!isFilled.keyText(page.data.name)) continue;

  const [firstName, ...lastName] = page.data.name.split(" ");
  page.data.first_name = firstName;
  page.data.last_name = lastName.join(" ");

  migration.updateDocument(page);
}

await writeClient.migrate(migration, {
  reporter: (event) => console.log(event),
});

Run it with Node:

node migrate.js

Then review and publish the Migration Release in Prismic.

3. Switch your code

Read the two new fields instead of the old one:

- <p>{page.data.name}</p>
+ <p>{page.data.first_name} {page.data.last_name}</p>

Merge your code changes into production.

4. Remove the old field

Once nothing uses it, remove the old field:

# Remove the old field
npx prismic field remove name --from-type page

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Make a single field repeatable

Sometimes you need to make a single field repeatable. For example, a blog post might link to one Author through a content relationship and later need to credit several authors. Most fields can't be repeated on their own, so add a repeatable group that holds the field, move the content into it, switch your code, then remove the old field.

1. Add the repeatable group

Add a group field, then add the content relationship inside it. The old author field stays untouched.

# Add the repeatable group and its content relationship
npx prismic field add group authors --to-type blog_post
npx prismic field add content-relationship authors.author --to-type blog_post --custom-type author --field name

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

2. Move the content over

Move each blog post's author into the group. You'll need a Prismic authentication token to use the Migration API. You can create one like this:

npx prismic token create --write

Save the token in a .env file like this:

PRISMIC_WRITE_TOKEN=your_write_token

Now write the migration script in a migrate.js file:

// migrate.js

import { createWriteClient, createMigration, isFilled } from "@prismicio/client";
import prismicConfig from "./prismic.config.json" with { type: "json" };

// Load .env into process.env (native, Node 20.12+)
process.loadEnvFile();

const writeClient = createWriteClient(prismicConfig.repositoryName, {
  writeToken: process.env.PRISMIC_WRITE_TOKEN,
});

const migration = createMigration();

// Fetch every blog post and copy the author relationship into the new group field
const posts = await writeClient.getAllByType("blog_post");

for (const post of posts) {
  if (!isFilled.contentRelationship(post.data.author)) continue;

  post.data.authors = [{ author: post.data.author }];

  migration.updateDocument(post);
}

await writeClient.migrate(migration, {
  reporter: (event) => console.log(event),
});

Run it with Node:

node migrate.js

Then review and publish the Migration Release in Prismic.

3. Switch your code

Map over the group instead of reading the single field:

- {isFilled.contentRelationship(post.data.author) && (
-   <span>{post.data.author.data.name}</span>
- )}
+ {post.data.authors.map((item) =>
+   isFilled.contentRelationship(item.author) ? (
+     <span key={item.author.id}>{item.author.data.name}</span>
+   ) : null,
+ )}

Merge your code changes into production.

4. Remove the old field

Once nothing uses it, remove the old field:

# Remove the old field
npx prismic field remove author --from-type blog_post

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Plain links are the exception. A plain link field can be made repeatable in place without losing content, but a content relationship like the author field above can't. Prismic keeps the existing value and serves it as a list with one item, so you can skip the steps above. Edit the field:

# Make the link field repeatable
npx prismic field edit cta --from-type page --repeatable

# Commit changes to Git
git add customtypes/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Then update your code to map over the list:

- <PrismicNextLink field={page.data.cta} />
+ {page.data.cta.map((link) => (
+   <PrismicNextLink key={link.key} field={link} />
+ ))}

Change the design of a slice

Sometimes you need to redesign a slice variation that already has content. For example, a call_to_action slice's withButton variation might need different fields for a new layout. Changing the variation in place would break every published page that uses it, so add a new variation alongside it, like withButtonV2.

There's no migration script this time. Keep the old variation in place and let content writers move pages to the new variation as they go, starting with the most important ones. Moving every page at once risks breaking layouts no one has reviewed, and most pages may not need the new design. Porting the important pages by hand keeps the risk low and the work small.

1. Add the new variation

Add the new variation to the slice, then model its new fields. The old withButton variation stays untouched.

# Add the new variation alongside the old one
npx prismic slice add-variation "With Button V2" --to call_to_action

# Commit changes to Git
git add src/slices/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

2. Build the new design

Render the new variation in your slice component:

  // Inside your CallToAction slice component
+ if (slice.variation === "withButtonV2") {
+   return <CallToActionV2 slice={slice} />
+ }

Merge your code changes into production.

3. Steer content writers to the new variation

Relabel the old variation so content writers reach for the new one:

# Mark the old variation as legacy
npx prismic slice edit-variation withButton --from-slice call_to_action --name "With Button (legacy)"

# Commit changes to Git
git add src/slices/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Content writers can now switch important pages to the new variation when they edit them. Pages that still use the old variation keep working.

Once no page uses the old variation, you can remove it:

# Remove the old variation once nothing uses it
npx prismic slice remove-variation withButton --from call_to_action

# Commit changes to Git
git add src/slices/* prismicio-types.d.ts

# Push changes to Prismic
npx prismic push

Wrap-up

The techniques in this guide share a few guidelines:

  • Add new fields freely; they don't affect existing content.
  • Use the Migration API to move content into the new fields.
  • Remove old fields only after migrating the content and updating your code.

These guidelines apply to any model change, not just the ones in this guide. Whatever you're changing, your published content stays intact and your website stays online.

To learn more about the tools used in this guide, see the Prismic documentation:

  • Prismic CLI: The commands used to model and push your changes.
  • Migrate to Prismic: How to write and run a migration script with the Migration API.
  • Content modeling: How model changes, including removals, affect published content.
Article written by

Angelo Ashmore

Senior Developer Experience Engineer

More posts

Join the discussion

Hit your website goals

Websites success stories from the Prismic Community

How Arcadia is Telling a Consistent Brand Story

Read Case Study

How Evri Cut their Time to Ship

Read Case Study

How Pallyy Grew Daily Visitors from 500 to 10,000

Read Case Study

From Powder to Pixels - Perfectly Planned Ski Vacations, Now Perfectly Digital

Read Case Study