├── .czrc ├── .editorconfig ├── .eslintignore ├── .github ├── renovate.json └── workflows │ ├── format-if-needed.yml │ ├── main.yml │ └── release-please.yml ├── .gitignore ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.config.ts ├── package.json ├── pnpm-lock.yaml ├── src ├── components │ ├── defaults.ts │ ├── list.ts │ ├── marks.ts │ ├── merge.ts │ └── unknown.ts ├── escape.ts ├── index.ts ├── to-html.ts ├── types.ts └── warnings.ts ├── test ├── escaping.test.ts ├── fixtures │ ├── 001-empty-block.ts │ ├── 002-single-span.ts │ ├── 003-multiple-spans.ts │ ├── 004-basic-mark-single-span.ts │ ├── 005-basic-mark-multiple-adjacent-spans.ts │ ├── 006-basic-mark-nested-marks.ts │ ├── 007-link-mark-def.ts │ ├── 008-plain-header-block.ts │ ├── 009-messy-link-text.ts │ ├── 010-basic-bullet-list.ts │ ├── 011-basic-numbered-list.ts │ ├── 012-image-support.ts │ ├── 013-materialized-image-support.ts │ ├── 014-nested-lists.ts │ ├── 015-all-basic-marks.ts │ ├── 016-deep-weird-lists.ts │ ├── 017-all-default-block-styles.ts │ ├── 018-marks-all-the-way-down.ts │ ├── 019-keyless.ts │ ├── 020-empty-array.ts │ ├── 021-list-without-level.ts │ ├── 022-inline-nodes.ts │ ├── 023-hard-breaks.ts │ ├── 024-inline-images.ts │ ├── 025-image-with-hotspot.ts │ ├── 026-inline-block-with-text.ts │ ├── 027-styled-list-items.ts │ ├── 028-custom-list-item-type.ts │ ├── 040-injection-link-href.ts │ ├── 050-custom-block-type.ts │ ├── 051-override-defaults.ts │ ├── 052-custom-marks.ts │ ├── 053-override-default-marks.ts │ ├── 060-list-issue.ts │ ├── 061-missing-mark-serializer.ts │ └── index.ts ├── mutations.test.ts ├── portable-text.test.ts └── serializers.test.ts ├── tsconfig.dist.json ├── tsconfig.json ├── tsconfig.settings.json └── typedoc.json /.czrc: -------------------------------------------------------------------------------- 1 | { 2 | "path": "cz-conventional-changelog" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; editorconfig.org 2 | root = true 3 | charset= utf8 4 | 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /coverage 3 | /demo/dist 4 | .nyc_output 5 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>sanity-io/renovate-config"], 4 | "packageRules": [ 5 | { 6 | "matchDepTypes": ["dependencies"], 7 | "matchPackageNames": ["@portabletext/toolkit", "@portabletext/types"], 8 | "rangeStrategy": "bump", 9 | "groupName": null, 10 | "groupSlug": null, 11 | "semanticCommitType": "fix" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/format-if-needed.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Auto format 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }} 10 | cancel-in-progress: true 11 | 12 | permissions: 13 | contents: read # for checkout 14 | 15 | jobs: 16 | run: 17 | name: Can the code be formatted? 🤔 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: pnpm/action-setup@v4 22 | - uses: actions/setup-node@v4 23 | with: 24 | cache: 'pnpm' 25 | node-version: lts/* 26 | - run: pnpm install --ignore-scripts 27 | - run: pnpm format 28 | - run: git restore .github/workflows CHANGELOG.md 29 | - uses: actions/create-github-app-token@v1 30 | id: generate-token 31 | with: 32 | app-id: ${{ secrets.ECOSCRIPT_APP_ID }} 33 | private-key: ${{ secrets.ECOSCRIPT_APP_PRIVATE_KEY }} 34 | - uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6 35 | with: 36 | author: github-actions <41898282+github-actions[bot]@users.noreply.github.com> 37 | body: I ran `pnpm format` 🧑‍💻 38 | branch: actions/format 39 | commit-message: 'chore(format): 🤖 ✨' 40 | delete-branch: true 41 | labels: 🤖 bot 42 | title: 'chore(format): 🤖 ✨' 43 | token: ${{ steps.generate-token.outputs.token }} 44 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | merge_group: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 14 | cancel-in-progress: true 15 | 16 | permissions: 17 | contents: read # for checkout 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | name: Lint & Build 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: pnpm/action-setup@v4 26 | - uses: actions/setup-node@v4 27 | with: 28 | cache: pnpm 29 | node-version: lts/* 30 | - run: pnpm install 31 | - run: pnpm type-check 32 | - run: pnpm lint 33 | - run: pnpm build 34 | 35 | test: 36 | runs-on: ${{ matrix.platform }} 37 | name: Node.js ${{ matrix.node-version }} / ${{ matrix.platform }} 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | platform: [macos-latest, ubuntu-latest, windows-latest] 42 | node-version: [lts/*] 43 | include: 44 | - platform: ubuntu-latest 45 | node-version: current 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: pnpm/action-setup@v4 49 | - uses: actions/setup-node@v4 50 | with: 51 | cache: pnpm 52 | node-version: ${{ matrix.node-version }} 53 | - run: pnpm install 54 | - run: pnpm test 55 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release Please 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | release-please: 14 | permissions: 15 | id-token: write # to enable use of OIDC for npm provenance 16 | # permissions for pushing commits and opening PRs are handled by the `generate-token` step 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/create-github-app-token@v1 20 | id: generate-token 21 | with: 22 | app-id: ${{ secrets.ECOSCRIPT_APP_ID }} 23 | private-key: ${{ secrets.ECOSCRIPT_APP_PRIVATE_KEY }} 24 | # This action will create a release PR when regular conventional commits are pushed to main, it'll also detect if a release PR is merged and npm publish should happen 25 | - uses: google-github-actions/release-please-action@v3 26 | id: release 27 | with: 28 | release-type: node 29 | token: ${{ steps.generate-token.outputs.token }} 30 | 31 | # Publish to NPM on new releases 32 | - uses: actions/checkout@v4 33 | if: ${{ steps.release.outputs.releases_created }} 34 | - uses: pnpm/action-setup@v4 35 | if: ${{ steps.release.outputs.releases_created }} 36 | - uses: actions/setup-node@v4 37 | if: ${{ steps.release.outputs.releases_created }} 38 | with: 39 | cache: pnpm 40 | node-version: lts/* 41 | registry-url: 'https://registry.npmjs.org' 42 | - run: pnpm install --ignore-scripts 43 | if: ${{ steps.release.outputs.releases_created }} 44 | - name: Set publishing config 45 | run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" 46 | if: ${{ steps.release.outputs.releases_created }} 47 | env: 48 | NODE_AUTH_TOKEN: ${{secrets.NPM_PUBLISH_TOKEN}} 49 | # Release Please has already incremented versions and published tags, so we just 50 | # need to publish the new version to npm here 51 | - run: pnpm publish 52 | if: ${{ steps.release.outputs.releases_created }} 53 | env: 54 | NPM_CONFIG_PROVENANCE: true 55 | # Publish to GitHub Pages 56 | - run: pnpm docs:build 57 | if: ${{ steps.release.outputs.releases_created }} 58 | - uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3 59 | if: ${{ steps.release.outputs.releases_created }} 60 | with: 61 | github_token: ${{ steps.generate-token.outputs.token }} 62 | publish_dir: ./docs 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # macOS finder cache file 40 | .DS_Store 41 | 42 | # VS Code settings 43 | .vscode 44 | 45 | # Lockfiles 46 | yarn.lock 47 | package-lock.json 48 | 49 | # Cache 50 | .cache 51 | 52 | # Compiled portable text library + demo 53 | /dist 54 | /docs 55 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | dist 3 | pnpm-lock.yaml 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 📓 Changelog 4 | 5 | All notable changes to this project will be documented in this file. See 6 | [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 7 | 8 | ## [2.0.14](https://github.com/portabletext/to-html/compare/v2.0.13...v2.0.14) (2025-02-06) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * **deps:** update dependency @portabletext/toolkit to ^2.0.17 ([#110](https://github.com/portabletext/to-html/issues/110)) ([9743f32](https://github.com/portabletext/to-html/commit/9743f328fe2593d5601d62e9dce81d0734d8115f)) 14 | 15 | ## [2.0.13](https://github.com/portabletext/to-html/compare/v2.0.12...v2.0.13) (2024-04-11) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **deps:** update dependency @portabletext/toolkit to ^2.0.15 ([#105](https://github.com/portabletext/to-html/issues/105)) ([3d57777](https://github.com/portabletext/to-html/commit/3d57777d35e4af153760be3af22101cd36b29d58)) 21 | * **deps:** update dependency @portabletext/types to ^2.0.13 ([#103](https://github.com/portabletext/to-html/issues/103)) ([e1238a9](https://github.com/portabletext/to-html/commit/e1238a9587a5d1354fb32de02cf77cdff12395a0)) 22 | 23 | ## [2.0.12](https://github.com/portabletext/to-html/compare/v2.0.11...v2.0.12) (2024-04-05) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * **deps:** update dependency @portabletext/toolkit to ^2.0.14 ([#96](https://github.com/portabletext/to-html/issues/96)) ([36fde76](https://github.com/portabletext/to-html/commit/36fde76a55c703bbc5cf8b72a3ae3e014d549b12)) 29 | 30 | ## [2.0.11](https://github.com/portabletext/to-html/compare/v2.0.10...v2.0.11) (2024-04-05) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * **deps:** update dependency @portabletext/types to ^2.0.12 ([#94](https://github.com/portabletext/to-html/issues/94)) ([1b9248b](https://github.com/portabletext/to-html/commit/1b9248bd64a71d22fdf42272608e586fdff36fcd)) 36 | 37 | ## [2.0.10](https://github.com/portabletext/to-html/compare/v2.0.9...v2.0.10) (2024-03-20) 38 | 39 | 40 | ### Bug Fixes 41 | 42 | * **deps:** update dependency @portabletext/toolkit to ^2.0.13 ([#89](https://github.com/portabletext/to-html/issues/89)) ([ec87d29](https://github.com/portabletext/to-html/commit/ec87d2983bb85860b4641ef423314fcd76c2bb77)) 43 | 44 | ## [2.0.9](https://github.com/portabletext/to-html/compare/v2.0.8...v2.0.9) (2024-03-20) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * **deps:** update dependency @portabletext/types to ^2.0.11 ([#87](https://github.com/portabletext/to-html/issues/87)) ([cbdfe6a](https://github.com/portabletext/to-html/commit/cbdfe6a40471c4645243750a61113b7168b86909)) 50 | 51 | ## [2.0.8](https://github.com/portabletext/to-html/compare/v2.0.7...v2.0.8) (2024-03-19) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * ship valid ESM ([805f981](https://github.com/portabletext/to-html/commit/805f9813ec63907107b0958501e60e54e019fc04)) 57 | 58 | ## [2.0.7](https://github.com/portabletext/to-html/compare/v2.0.6...v2.0.7) (2024-03-18) 59 | 60 | 61 | ### Bug Fixes 62 | 63 | * **deps:** update dependency @portabletext/toolkit to ^2.0.12 ([#80](https://github.com/portabletext/to-html/issues/80)) ([478db4a](https://github.com/portabletext/to-html/commit/478db4a82d0152345d18bf3b4e63c08d62978b84)) 64 | * **deps:** update dependency @portabletext/types to ^2.0.10 ([#77](https://github.com/portabletext/to-html/issues/77)) ([52785ed](https://github.com/portabletext/to-html/commit/52785ed4e2ac90c7ebc1b9a112bd18fb6c92ebd5)) 65 | 66 | ## [2.0.6](https://github.com/portabletext/to-html/compare/v2.0.5...v2.0.6) (2024-03-16) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * **deps:** update dependency @portabletext/toolkit to ^2.0.11 ([#74](https://github.com/portabletext/to-html/issues/74)) ([3bf4f9c](https://github.com/portabletext/to-html/commit/3bf4f9cd4eb022178dcf7e535a60fefa6aadcb3f)) 72 | * **deps:** update dependency @portabletext/types to ^2.0.9 ([#71](https://github.com/portabletext/to-html/issues/71)) ([2844f40](https://github.com/portabletext/to-html/commit/2844f40f67d3a03e80433f3577a73bf5d80ad797)) 73 | 74 | ## [2.0.5](https://github.com/portabletext/to-html/compare/v2.0.4...v2.0.5) (2023-10-10) 75 | 76 | 77 | ### Bug Fixes 78 | 79 | * **deps:** update dependency @portabletext/toolkit to ^2.0.10 ([#55](https://github.com/portabletext/to-html/issues/55)) ([3067019](https://github.com/portabletext/to-html/commit/3067019df8ef3f323075b728afb9d78ce976f6cd)) 80 | * **deps:** update dependency @portabletext/types to ^2.0.8 ([#53](https://github.com/portabletext/to-html/issues/53)) ([41e7acb](https://github.com/portabletext/to-html/commit/41e7acb83d7059c6b64c2a3b7685ee921e430fcd)) 81 | 82 | ## [2.0.4](https://github.com/portabletext/to-html/compare/v2.0.3...v2.0.4) (2023-09-28) 83 | 84 | 85 | ### Bug Fixes 86 | 87 | * **deps:** update dependency @portabletext/toolkit to ^2.0.9 ([#43](https://github.com/portabletext/to-html/issues/43)) ([11850ac](https://github.com/portabletext/to-html/commit/11850acd4d38587666b652da55f36ae2a1a17409)) 88 | * **deps:** update dependency @portabletext/types to ^2.0.7 ([#44](https://github.com/portabletext/to-html/issues/44)) ([9d17d6e](https://github.com/portabletext/to-html/commit/9d17d6e642dca5d4f20e29ce4e3a58dce3577761)) 89 | 90 | ## [2.0.3](https://github.com/portabletext/to-html/compare/v2.0.2...v2.0.3) (2023-08-24) 91 | 92 | ### Bug Fixes 93 | 94 | - **deps:** update dependency @portabletext/toolkit to ^2.0.8 ([#32](https://github.com/portabletext/to-html/issues/32)) ([6d0d4c4](https://github.com/portabletext/to-html/commit/6d0d4c4347ed2dd3c4df07f4650289533de41188)) 95 | - **deps:** update dependency @portabletext/types to ^2.0.6 ([#33](https://github.com/portabletext/to-html/issues/33)) ([4a59061](https://github.com/portabletext/to-html/commit/4a5906140f797f464183a0f4e2fdb0a162153f74)) 96 | 97 | ## [2.0.2](https://github.com/portabletext/to-html/compare/v2.0.1...v2.0.2) (2023-08-23) 98 | 99 | ### Bug Fixes 100 | 101 | - add provenance ([27ff7a8](https://github.com/portabletext/to-html/commit/27ff7a8f8dd2879c5ee5b2baa53c48fec3d480c1)) 102 | 103 | ## [2.0.1](https://github.com/portabletext/to-html/compare/v2.0.0...v2.0.1) (2023-08-23) 104 | 105 | ### Bug Fixes 106 | 107 | - **esm:** add `node.module` condition ([#22](https://github.com/portabletext/to-html/issues/22)) ([9676248](https://github.com/portabletext/to-html/commit/9676248e5bc6dc8aec85e3dcc0a2853591ffa5d4)) 108 | 109 | ## [2.0.0](https://github.com/portabletext/to-html/compare/v1.0.4...v2.0.0) (2023-02-17) 110 | 111 | ### ⚠ BREAKING CHANGES 112 | 113 | - Only node 14.13.1 and higher is now supported. 114 | - ESM/CommonJS compatibility is now improved, but may cause new behavior 115 | in certain contexts. It should however be more forward-compatible, and allow usage in 116 | a wider range of tools and environments. 117 | 118 | ADDED: Semantic release automation 119 | 120 | ADDED: Use @sanity/pkg-utils instead of vite directly, for better control of bundling 121 | 122 | ### Features 123 | 124 | - improve ESM/CJS compatibility, drop support for node 12 ([#5](https://github.com/portabletext/to-html/issues/5)) ([aabcfb5](https://github.com/portabletext/to-html/commit/aabcfb538586943d834a6b87f5572f23e8942fb1)) 125 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sanity.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @portabletext/to-html 2 | 3 | [![npm version](https://img.shields.io/npm/v/@portabletext/to-html.svg?style=flat-square)](https://www.npmjs.com/package/@portabletext/to-html)[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@portabletext/to-html?style=flat-square)](https://bundlephobia.com/result?p=@portabletext/to-html) 4 | 5 | Render [Portable Text](https://portabletext.org/) to HTML. 6 | 7 | - Using **React**? See [@portabletext/react](https://github.com/portabletext/react-portabletext) 8 | - Using **Svelte**? See [@portabletext/svelte](https://github.com/portabletext/svelte-portabletext) 9 | 10 | ## Installation 11 | 12 | ``` 13 | npm install --save @portabletext/to-html 14 | ``` 15 | 16 | ## Basic usage 17 | 18 | ```js 19 | import {toHTML} from '@portabletext/to-html' 20 | 21 | console.log( 22 | toHTML(portableTextBlocks, { 23 | components: { 24 | /* optional object of custom components to use */ 25 | }, 26 | }), 27 | ) 28 | ``` 29 | 30 | ## Styling the output 31 | 32 | The rendered HTML does not have any styling applied, so you will want to either render a parent container with a class name you can target in your CSS, or pass [custom components](#customizing-components) if you want to pass specific class names. 33 | 34 | ## Customizing components 35 | 36 | "Components" are (in this package) just functions, which receive an object of properties that contains the relevant information needed to make a decision about what to render. The return value of the component functions are plain strings containing HTML. 37 | 38 | Default component functions are provided for all standard features of the Portable Text spec, with logical HTML defaults. 39 | 40 | You can pass an object of component functions to use in the `components` option, both to override the defaults and to provide components for your custom content types. 41 | 42 | The passed components will be merged with the default components. 43 | 44 | **⚠️ IMPORTANT: Make sure you sanitize/escape the returned HTML!** 45 | 46 | In the below examples we use the [htm](https://github.com/developit/htm) and [vhtml](https://github.com/developit/vhtml) modules to render "safe" HTML. 47 | 48 | ```js 49 | import htm from 'htm' 50 | import vhtml from 'vhtml' 51 | import {toHTML, uriLooksSafe} from '@portabletext/to-html' 52 | 53 | const html = htm.bind(vhtml) 54 | 55 | const myPortableTextComponents = { 56 | types: { 57 | image: ({value}) => html``, 58 | callToAction: ({value, isInline}) => 59 | isInline 60 | ? html`${value.text}` 61 | : html`
${value.text}
`, 62 | }, 63 | 64 | marks: { 65 | link: ({children, value}) => { 66 | // ⚠️ `value.href` IS NOT "SAFE" BY DEFAULT ⚠️ 67 | // ⚠️ Make sure you sanitize/validate the href! ⚠️ 68 | const href = value.href || '' 69 | 70 | if (uriLooksSafe(href)) { 71 | const rel = href.startsWith('/') ? undefined : 'noreferrer noopener' 72 | return html`${children}` 73 | } 74 | 75 | // If the URI appears unsafe, render the children (eg, text) without the link 76 | return children 77 | }, 78 | }, 79 | } 80 | 81 | console.log(toHTML(somePtValue, {components: myPortableTextComponents})) 82 | ``` 83 | 84 | ## Available components 85 | 86 | These are the overridable/implementable keys: 87 | 88 | ### `types` 89 | 90 | An object of component functions that renders different types of objects that might appear both as part of the input array, or as inline objects within text blocks - eg alongside text spans. 91 | 92 | Use the `isInline` property to check whether or not this is an inline object or a block. 93 | 94 | The object has the shape `{typeName: ComponentFn}`, where `typeName` is the value set in individual `_type` attributes. 95 | 96 | ### `marks` 97 | 98 | Object of component functions that renders different types of marks that might appear in spans. Marks can be either be simple "decorators" (eg emphasis, underline, italic) or full "annotations" which include associated data (eg links, references, descriptions). 99 | 100 | If the mark is a decorator, the component function will receive a `markType` property which has the name of the decorator (eg `em`). If the mark is an annotation, it will receive both a `markType` with the associated `_type` property (eg `link`), and a `value` property with an object holding the data for this mark. 101 | 102 | The component function also receives a `children` property that should (usually) be rendered in whatever parent container makes sense for this mark (eg ``, ``). 103 | 104 | ### `block` 105 | 106 | An object of component functions that renders portable text blocks with different `style` properties. The object has the shape `{styleName: ComponentFn}`, where `styleName` is the value set in individual `style` attributes on blocks (`normal` being the default). 107 | 108 | Can also be set to a single component function, which would handle block styles of _any_ type. 109 | 110 | ### `list` 111 | 112 | Object of component functions used to render lists of different types (`bullet` vs `number`, for instance, which by default is `