add skills
Build and Deploy / build-and-deploy (push) Successful in 2m53s

This commit is contained in:
2026-05-18 08:39:42 +02:00
parent 92a80e8759
commit 57af0b8386
53 changed files with 10190 additions and 0 deletions
@@ -0,0 +1,125 @@
---
title: Serialize Portable Text to Vue
description: Render Portable Text in Vue 3 and Nuxt using @portabletext/vue
tags: [portable-text, vue, nuxt, serialization, rendering]
---
# Serialize Portable Text to Vue
Use `@portabletext/vue` to render PT in Vue 3 / Nuxt applications.
```bash
npm install @portabletext/vue
```
## Basic Usage
```vue
<script setup lang="ts">
import {PortableText} from '@portabletext/vue'
import type {PortableTextBlock} from '@portabletext/types'
const props = defineProps<{value: PortableTextBlock[]}>()
</script>
<template>
<PortableText :value="value" :components="components" />
</template>
```
## Custom Components
Vue components can be defined as render functions, SFCs, or JSX.
### Render Function Style (Concise)
```ts
import {h} from 'vue'
import type {PortableTextVueComponents} from '@portabletext/vue'
const components: PortableTextVueComponents = {
types: {
image: ({value}) => h('img', {src: urlFor(value).width(800).url(), alt: value.alt || ''}),
code: ({value}) => h('pre', {'data-language': value.language}, h('code', value.code)),
},
marks: {
link: ({value}, {slots}) => {
const rel = !value?.href?.startsWith('/') ? 'noreferrer noopener' : undefined
return h('a', {href: value?.href, rel}, slots.default?.())
},
highlight: (_, {slots}) => h('span', {class: 'bg-yellow-200'}, slots.default?.()),
},
block: {
h1: (_, {slots}) => h('h1', {class: 'text-4xl font-bold'}, slots.default?.()),
h2: (_, {slots}) => h('h2', {class: 'text-3xl font-semibold'}, slots.default?.()),
blockquote: (_, {slots}) => h('blockquote', {class: 'border-l-4 pl-4 italic'}, slots.default?.()),
},
list: {
bullet: (_, {slots}) => h('ul', {class: 'list-disc ml-6'}, slots.default?.()),
number: (_, {slots}) => h('ol', {class: 'list-decimal ml-6'}, slots.default?.()),
},
}
```
### SFC Style (For Complex Components)
```vue
<!-- ImageBlock.vue -->
<script setup lang="ts">
import type {PortableTextComponentProps} from '@portabletext/vue'
const props = defineProps<PortableTextComponentProps<{
asset: {_ref: string}
alt?: string
caption?: string
}>>()
</script>
<template>
<figure>
<img :src="urlFor(value).width(800).url()" :alt="value.alt || ''" />
<figcaption v-if="value.caption">{{ value.caption }}</figcaption>
</figure>
</template>
```
Then register:
```ts
import ImageBlock from './ImageBlock.vue'
const components = {
types: {
image: ImageBlock,
},
}
```
## Props Pattern
Custom components receive:
| Prop | Description |
|------|-------------|
| `value` | The block/mark data |
| `index` | Position in parent array |
| `isInline` | Whether this is an inline element |
| `renderNode` | Internal renderer (rarely needed) |
Children are passed via **slots** (`slots.default?.()`), not props.
## Plain Text Extraction
```ts
import {toPlainText} from '@portabletext/vue'
const text = toPlainText(blocks)
```
## Reference
- [@portabletext/vue](https://github.com/portabletext/vue-portabletext)
- [Sanity + Nuxt guide](https://www.sanity.io/guides/sanity-nuxt)