How to override annotation styling in Portable Text Editor without losing functionality?

10 replies
Last updated: Jan 28, 2026
Hi all! We're using three kinds of annotation for a particular document: internal links, external links, and defined terms. In the Portable Text Editor, I would like to render the annotated text with a different color for each kind of annotation. I've built custom annotation preview components, but I can't figure out how to override the styling given by
renderDefault
.
So, to put it simply, how can I override the styling given by
{props.renderDefault(props.children)}
without losing functionality?
AI Update

The trick to overriding the default styling from renderDefault while keeping its functionality is to wrap the rendered output in a custom element (like a span) with your own CSS classes. The renderDefault function applies Sanity's default styles to its children, but you can target those children with more specific CSS selectors to override them.

Here's the approach that works:

1. Create a custom annotation component that wraps renderDefault:

import { BlockAnnotationProps } from 'sanity'
import styles from './CustomAnnotation.module.css'

export const CustomAnnotationRenderer = (props: BlockAnnotationProps) => (
  <span className={styles.customAnnotation}>{props.renderDefault(props)}</span>
)

2. Use CSS to target the children elements and override Sanity's default styles:

The key is using the child selector (*) to target the elements that renderDefault creates. You'll also want to use !important to ensure your styles override Sanity's defaults. Here's an example that changes colors based on theme:

*[data-scheme='light'] .customAnnotation * {
  border-color: #c123fc !important;
  background-color: #f8e9fe;
}

*[data-scheme='dark'] .customAnnotation * {
  border-color: #d97bfd !important;
  background-color: #331440;
  color: #fff;
}

3. Register the custom component in your schema:

defineField({
  name: 'internalLink',
  title: 'Internal Link',
  type: 'reference',
  to: [{ type: 'page' }],
  components: {
    annotation: CustomAnnotationRenderer,
  },
})

For your use case with three different annotation types (internal links, external links, and defined terms), you'd create three separate renderer components, each with its own CSS module defining different colors. The wrapper span approach lets you scope your styles while renderDefault handles all the editor functionality like click interactions, hover states, and accessibility features.

You can inspect the rendered elements in your browser's developer tools to see exactly what HTML structure renderDefault creates, which helps you write more targeted CSS selectors if needed.

This solution comes from this community discussion where someone had the exact same need. For more context on customizing Portable Text annotations, check out the ultimate guide for customizing Portable Text.

Show original thread
10 replies

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?