Internal Link Error in Next.js Application: Unknown mark type "internalLink"

11 replies
Last updated: Aug 14, 2022
Piggybacking off the above, when adding an internal link with
@portabletext/react@^1.0.6
and build my nextjs application, I get
Unknown mark type "internalLink", specify a component for it in the
components.marks
option
However, I am doing
const myPortableTextComponents = {
  marks: {
    internalLink: ({ children, value }) => {
      return (
        <Link href={`/${value.slug.current}`} prefetch={false}>
          <a>{children}</a>
        </Link>
      )
    },
  },
}

export const PortableText = (props) => (
  <PortableTextComponent components={myPortableTextComponents} {...props} />
)
It doesn't break the build but just curious why if I am defining it?
AI Update

The warning you're seeing should not appear if your component is correctly defined. This warning indicates that @portabletext/react is encountering an internalLink mark type in your content but isn't finding the corresponding component definition—even though you think you've defined it.

Here are the most likely causes and solutions:

1. Component Definition Isn't Being Applied

The most common issue is that your myPortableTextComponents object isn't actually being passed to the PortableText component properly. Double-check that:

  • You're importing and using your custom PortableText wrapper component (not accidentally importing the original from @portabletext/react elsewhere)
  • The components prop is actually being passed through with {...props}

2. Schema Mismatch

The mark type name in your Sanity schema must exactly match the key in your components definition. According to the @portabletext/react documentation, verify in your Sanity schema that you've defined the annotation as:

{
  type: 'object',
  name: 'internalLink',  // Must match exactly
  // ... rest of schema
}

Even a small typo or casing difference (InternalLink vs internalLink) will cause this warning.

Your code has a Next.js issue that could cause problems. In Next.js 13+, you should not nest an <a> tag inside <Link>. Update your component to:

const myPortableTextComponents = {
  marks: {
    internalLink: ({ children, value }) => {
      return (
        <Link href={`/${value?.slug?.current || ''}`} prefetch={false}>
          {children}
        </Link>
      )
    },
  },
}

Note: I also added optional chaining (value?.slug?.current) to prevent errors if the data structure isn't what you expect.

4. Controlling Warning Behavior

You can control how the library handles missing components using the onMissingComponent prop:

<PortableTextComponent 
  components={myPortableTextComponents} 
  onMissingComponent={(message, options) => {
    console.warn(message, options)
  }}
  {...props} 
/>

However, this is just for debugging—the warning shouldn't appear in the first place if everything is configured correctly.

Verification Steps

  1. Check your browser's rendered output—are the internal links actually working and rendering correctly?
  2. If they ARE rendering correctly, the issue might be with how you're passing components in a specific instance
  3. If they're NOT rendering correctly (showing as plain text), then the component truly isn't being found

The key point: this warning means the library genuinely can't find your component definition. It's not "informational" or "expected"—it indicates a configuration mismatch that needs to be resolved. Make sure your schema annotation name exactly matches your component key, and verify that your custom PortableText wrapper is actually being used throughout your application.

I think you missed one thing: A “link” is expressed as a mark annotation.
Maybe it’s because you are spreading the props?I’d do
<PortableTextComponent value={value} components={myPortableTextComponents} />
to avoid any confusion when I use it.
You might also have a conflict with your
PortableText
because it’s a named export.Or have you renamed the import?
import { PortableText as SomethingElse } from '@portabletext/react'

Something like that would also avoid confusion:

export const CustomPortableText = ({ value }) => (
  <PortableText value={value} components={myPortableTextComponents} />
)
I’ll try the value suggestion and yes I renamed the import
So I am doing
export const PortableTextComponent = ({ value }) => (
  <PortableText components={myPortableTextComponents} value={value} />
)
but I still get

Unknown mark type "internalLink", specify a component for it in the `components.marks` option
Even adding
onMissingComponent={false}
does nothing...
Can you share your schema?
import { PlugIcon } from "@sanity/icons";export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
{
title: "Block",
type: "block",
// Styles let you set what your user can mark up blocks with. These
// correspond with HTML tags, but you can set any title or value
// you want and decide how you want to deal with it where you want to
// use your content.
styles: [
{ title: "Normal", value: "normal" },
{ title: "H1", value: "h1" },
{ title: "H2", value: "h2" },
{ title: "H3", value: "h3" },
{ title: "H4", value: "h4" },
{ title: "Quote", value: "blockquote" },
],
lists: [{ title: "Bullet", value: "bullet" }],
// Marks let you mark up inline text in the block editor.
marks: {
// Decorators usually describe a single property – e.g. a typographic
// preference or highlighting by editors.
decorators: [
{ title: "Strong", value: "strong" },
{ title: "Emphasis", value: "em" },
],
// Annotations can be any object structure – e.g. a link or a footnote.
annotations: [
{
title: "URL",
name: "link",
type: "object",
fields: [
{
title: "URL",
name: "href",
type: "url",
validation: (Rule) =&gt;
Rule.uri({
scheme: ["http", "https", "mailto", "tel"],
}),
},
],
},
{
name: "internalLink",
title: "Internal Link",
type: "object",
icon: PlugIcon,
fields: [
{
title: "Reference",
name: "reference",
type: "reference",
to: [{ type: "post" }],
},
],
},
],
},
},
// You can add additional types here. Note that you can't use
// primitive types such as 'string' and 'number' in the same array
// as a block type.
{
type: "image",
options: { hotspot: true, metaData: ["blurhash", "lqip"] },
},
{
type: "player",
},
{
type: "twitter",
},
],
};
Well that doesn't format nicely on mobile 🥲
Nothing wrong here, do you get any result from your blockContent when you fetch your data?
Yup everything works as expected

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?