CoursesMigrating from Webflow to SanityHandle internal links in Portable Text
Migrating from Webflow to Sanity

Handle internal links in Portable Text

Choose between raw URL strings and document references for internal links, then build the URL map your extraction scripts will use.
Log in to mark your progress for each Lesson and Task

Before writing any extraction scripts, decide how internal links inside rich text become links in Sanity. There are two options.

The link stays as a URL path (/blog/some-post). Simple to implement, but fragile when slugs change.

Define a custom internalLink mark type that stores a reference to a Sanity document. The frontend resolves the reference to the current URL at render time. This is the better long-term choice. Editors get autocomplete when inserting links, and renamed slugs don't silently break.

@sanity/schemaTypes/blockContent.ts @sanity/schemaTypes/index.ts I want to add an internalLink annotation to my Portable Text schema that lets editors link to internal documents by reference. Looking at the document types registered in index.ts, add an internalLink mark type to blockContent.ts that references all document types that have a public-facing URL (i.e. have a slug field). Output the updated blockContent.ts file.

The AI reads your actual registered document types and generates the to array from them — so it references caseStudy and solution if those are your types, not a generic page and post. The output should look something like this, with your own type names:

// sanity/schemaTypes/blockContent.ts — internalLink added to annotations
{
name: 'internalLink',
type: 'object',
title: 'Internal Link',
fields: [
{
name: 'reference',
type: 'reference',
to: [
// Your actual document types with slug fields go here
{ type: 'page' },
{ type: 'caseStudy' },
]
}
]
}

url-map.json is a file your import script reads at runtime. When rich-text.js processes an <a href="/blog/some-post"> tag, it checks url-map.json to find the matching Sanity document _id and emits an internalLink reference annotation instead of a plain external link. Without it, every internal link in your rich text is imported as a plain URL.

This is different from your redirect list. Redirects handle external traffic after DNS transfer. url-map.json is used purely during migration to wire up internal references.

You'll generate this file in Chapter 5 after running import pass 1. The step-by-step instructions are there, in the right order.

If you already imported with plain link annotations and want to convert them after the fact, a targeted patch script is often simpler than re-running the full import. The pattern: fetch all documents containing link markDefs from Sanity, build a slug-to-_id map from your imported documents, iterate over every block's markDefs, and for each link whose href resolves to a known document _id, replace the markDef with an internalLink reference and update _type on any child spans that reference that key. Use patch.set({ content: updatedBlocks }) to write the result back. This is idempotent, running it again after re-imports keeps everything in sync.

Next, you’ll fetch your collection items from the API.

Mark lesson as complete
You have 1 uncompleted task in this lesson
0 of 1