5.8 KiB
title, description, categories, keywords, action
| title | description | categories | keywords | action | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| transform.PortableText | Converts Portable Text to Markdown. |
|
{{< new-in "0.145.0" />}}
Portable Text is a JSON structure that represent rich text content in the Sanity CMS. In Hugo, this function is typically used in a Content Adapter that creates pages from Sanity data.
Types supported
blockandspanimage. Note that the image handling is currently very simple; we link to theasset.urlusingasset.altTextas the image alt text andasset.titleas the title. For more fine grained control you may want to process the images in a image render hook.code(see the code-input plugin). Code will be rendered as fenced code blocks with any filename provided passed on as a markdown attribute.
{{< note >}} Since the Portable Text gets converted to Markdown before it gets passed to Hugo, rendering of links, headings, images and code blocks can be controlled with Render Hooks. {{< /note >}}
Example
Content Adapter
{{< code file=content/_content.gotmpl >}} {{ $projectID := "mysanityprojectid" }} {{ $useCached := true }} {{ $api := "api" }} {{ if $useCached }} {{/* See https://www.sanity.io/docs/api-cdn */}} {{ $api = "apicdn" }} {{ end }} {{ $url := printf "https://%s.%s.sanity.io/v2021-06-07/data/query/production" $projectID $api }}
{{/* prettier-ignore-start / -}}
{{ $q := *[_type == 'post']{ title, publishedAt, summary, slug, body[]{ ..., _type == "image" => { ..., asset->{ _id, path, url, altText, title, description, metadata { dimensions { aspectRatio, width, height } } } } }, }
}}
{{/ prettier-ignore-end */ -}}
{{ $body := dict "query" $q | jsonify }}
{{ $opts := dict "method" "post" "body" $body }}
{{ $r := resources.GetRemote $url $opts }}
{{ $m := $r | transform.Unmarshal }}
{{ $result := $m.result }}
{{ range $result }}
{{ if not .slug }}
{{ continue }}
{{ end }}
{{ $markdown := transform.PortableText .body }}
{{ $content := dict
"mediaType" "text/markdown"
"value" $markdown
}}
{{ $params := dict
"portabletext" (.body | jsonify (dict "indent" " "))
}}
{{ $page := dict
"content" $content
"kind" "page"
"path" .slug.current
"title" .title
"date" (.publishedAt | time )
"summary" .summary
"params" $params
}}
{{ $.AddPage $page }}
{{ end }}
{{< /code >}}
Sanity setup
Below outlines a suitable Sanity studio setup for the above example.
{{< code file=sanity.config.ts >}} import {defineConfig} from 'sanity' import {structureTool} from 'sanity/structure' import {visionTool} from '@sanity/vision' import {schemaTypes} from './schemaTypes' import {media} from 'sanity-plugin-media' import {codeInput} from '@sanity/code-input'
export default defineConfig({ name: 'default', title: 'my-sanity-project',
projectId: 'mysanityprojectid', dataset: 'production',
plugins: [structureTool(), visionTool(), media(),codeInput()],
schema: { types: schemaTypes, }, })
{{< /code >}}
Type/schema definition:
{{< code file=schemaTypes/postType.ts >}} import {defineField, defineType} from 'sanity'
export const postType = defineType({ name: 'post', title: 'Post', type: 'document', fields: [ defineField({ name: 'title', type: 'string', validation: (rule) => rule.required(), }), defineField({ name: 'summary', type: 'string', validation: (rule) => rule.required(), }), defineField({ name: 'slug', type: 'slug', options: {source: 'title'}, validation: (rule) => rule.required(), }), defineField({ name: 'publishedAt', type: 'datetime', initialValue: () => new Date().toISOString(), validation: (rule) => rule.required(), }), defineField({ name: 'body', type: 'array', of: [ { type: 'block', }, { type: 'image' }, { type: 'code', options: { language: 'css', languageAlternatives: [ {title: 'HTML', value: 'html'}, {title: 'CSS', value: 'css'}, ], withFilename: true, }, }, ], }), ], }) {{< /code >}}
Note that the above requires some additional plugins to be installed:
npm i sanity-plugin-media @sanity/code-input
{{< code file=schemaTypes/index.ts >}} import {postType} from './postType'
export const schemaTypes = [postType] {{< /code >}}
Server setup
Unfortunately, Sanity's API does not support RFC 7234 and their output changes even if the data has not. A recommended setup is therefore to use their cached apicdn endpoint (see above) and then set up a reasonable polling and file cache strategy in your Hugo configuration, e.g:
{{< code-toggle file=hugo >}} [HTTPCache] HTTPCache.polls disable = false low = '30s' high = '3m' [HTTPCache.polls.for] includes = ['https://..sanity.io/**']
[caches.getresource] dir = ':cacheDir/:project' maxAge = "5m" {{< /code-toggle >}}
The polling above will be used when running the server/watch mode and rebuild when you push new content in Sanity.
See Caching in resources.GetRemote for more fine grained control.