Render [Portable Text](https://portabletext.org) block content with [Svelte](https://svelte.dev/) components.
npm install @portabletext/svelteRender Portable Text block content with Svelte components.
npm i @portabletext/svelte -D
⚠️ Svelte 5.0.0 or higher is required.
``svelte
// Portable Text array ...
]}
/>
`
This is enough to get you set-up with basic block content with formatting and text styles. When working with images, custom styles, blocks & marks, though, you'll need to customize your renderer with components:
You can use the components prop to determine how the renderer should process each block, mark or style type.
`svelte`
// Portable Text array ...
]}
components={{
types: {
// block-level components
callout: Callout,
// inline-level components
userInfo: UserInfo
},
marks: {
absUrl: AbsoluteURL,
// Overwrite default mark renderers
strong: CustomStrong
},
block: {
normal: CustomParagraph,
blockquote: Quote,
// Re-using the same component across multiple styles
h1: CustomHeading,
h2: CustomHeading,
h3: CustomHeading,
// Custom user-defined style
textCenter: CentralizedText
},
list: {
// Swap only the list parts you need
bullet: UnorderedListWrapper,
// Custom user-defined list type
checklist: ChecklistWrapper
},
listItem: {
bullet: ListItem,
checklist: ChecklistItem
}
}}
/>
Example components from above:
`svelte
{#if portableText.value.bold}
{userName}
{:else}
{userName}
{/if}
`
`svelte
{#if value.url}
{@render children()}
{:else}
{@render children()}
{/if}
`
> 📌 To keep in mind: Svelte's SSR mode seems to have issues with whitespace (see #3168), where it does strip unnecessary space between components. Due to this, marks (formatting, links, etc.) some times are rendered incorrectly. We're tracking this in #1.
`svelte
The component above is also an example of how you can access blocks surrounding the current one for rule-based design.
Finally, you can pass
context to your component to have custom external data exposed to all components. This is useful in scenarios such as:- Adding different styles to the same block depending on its placement
- Loading in data from an external source/API
- Running expensive calculations on your
value only onceHere's a complete example with a
footnote annotation, where editors focus on writing its contents, and the front-end smartly position it and define its number:`svelte
value={value}
components={{
marks: {
footnote: Footnote
}
}}
context={{
// Pass these footnotes inside the context
footnotes
}}
/>
{#each footnotes as note}
value={note.note}
components={{
marks: {
link: Link
}
}}
/>
👆
{/each}
{@render children()}
#note-${portableText.value._key}}>{number}
`Disabling warnings / handling unknown types
When the library encounters a block, mark, list or list item with a type that is not known (eg it has no corresponding component in the
components property), it will by default print a console warning.To disable this behavior, you can either pass
false to the onMissingComponent property, or give it a custom function you want to use to report the error. For instance:`jsx
import {PortableText} from '@portabletext/svelte' value={[/ array of portable text blocks /]}
onMissingComponent={false}
/>
// or, pass it a function:
value={[/ array of portable text blocks /]}
onMissingComponent={(message, options) => {
myErrorLogger.report(message, {
// eg
someUnknownType
type: options.type, // 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle'
nodeType: options.nodeType
})
}}
/>
`Rendering Plain Text
This module also exports a function (
toPlainText()) that will render one or more Portable Text blocks as plain text. This is helpful in cases where formatted text is not supported, or you need to process the raw text value.For instance, to render an OpenGraph meta description for a page:
`svelte
`Credits
Big thanks to Emma Agering, Jacob Størdahl, Ollie Taylor, Rune, Stephane Vanraes & Toby Milner-Gulland (alphabetical order) for working on their custom renderers while we didn't have an official one. You've helped many ship Svelte + Sanity projects!
License
MIT-licensed. See LICENSE.