{"id":13876157,"url":"https://github.com/portabletext/to-html","last_synced_at":"2025-04-04T08:09:22.523Z","repository":{"id":53833184,"uuid":"455394413","full_name":"portabletext/to-html","owner":"portabletext","description":"Render Portable Text to HTML using Javascript","archived":false,"fork":false,"pushed_at":"2025-02-14T12:26:09.000Z","size":1016,"stargazers_count":53,"open_issues_count":4,"forks_count":6,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-03-28T07:04:53.081Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/portabletext.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-04T02:07:49.000Z","updated_at":"2025-03-10T06:52:40.000Z","dependencies_parsed_at":"2023-10-01T23:36:15.239Z","dependency_job_id":"3ec1e097-3b1f-401c-b51a-44764262cca8","html_url":"https://github.com/portabletext/to-html","commit_stats":{"total_commits":24,"total_committers":3,"mean_commits":8.0,"dds":"0.20833333333333337","last_synced_commit":"a1e302c087266a69d73505833029c7ff9885e856"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portabletext%2Fto-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portabletext%2Fto-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portabletext%2Fto-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/portabletext%2Fto-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/portabletext","download_url":"https://codeload.github.com/portabletext/to-html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247142032,"owners_count":20890651,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T06:01:04.987Z","updated_at":"2025-04-04T08:09:22.500Z","avatar_url":"https://github.com/portabletext.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","others"],"sub_categories":[],"readme":"# @portabletext/to-html\n\n[![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)\n\nRender [Portable Text](https://portabletext.org/) to HTML.\n\n- Using **React**? See [@portabletext/react](https://github.com/portabletext/react-portabletext)\n- Using **Svelte**? See [@portabletext/svelte](https://github.com/portabletext/svelte-portabletext)\n\n## Installation\n\n```\nnpm install --save @portabletext/to-html\n```\n\n## Basic usage\n\n```js\nimport {toHTML} from '@portabletext/to-html'\n\nconsole.log(\n  toHTML(portableTextBlocks, {\n    components: {\n      /* optional object of custom components to use */\n    },\n  }),\n)\n```\n\n## Styling the output\n\nThe 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.\n\n## Customizing components\n\n\"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.\n\nDefault component functions are provided for all standard features of the Portable Text spec, with logical HTML defaults.\n\nYou 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.\n\nThe passed components will be merged with the default components.\n\n**⚠️ IMPORTANT: Make sure you sanitize/escape the returned HTML!**\n\nIn the below examples we use the [htm](https://github.com/developit/htm) and [vhtml](https://github.com/developit/vhtml) modules to render \"safe\" HTML.\n\n```js\nimport htm from 'htm'\nimport vhtml from 'vhtml'\nimport {toHTML, uriLooksSafe} from '@portabletext/to-html'\n\nconst html = htm.bind(vhtml)\n\nconst myPortableTextComponents = {\n  types: {\n    image: ({value}) =\u003e html`\u003cimg src=\"${value.imageUrl}\" /\u003e`,\n    callToAction: ({value, isInline}) =\u003e\n      isInline\n        ? html`\u003ca href=\"${value.url}\"\u003e${value.text}\u003c/a\u003e`\n        : html`\u003cdiv class=\"callToAction\"\u003e${value.text}\u003c/div\u003e`,\n  },\n\n  marks: {\n    link: ({children, value}) =\u003e {\n      // ⚠️ `value.href` IS NOT \"SAFE\" BY DEFAULT ⚠️\n      // ⚠️ Make sure you sanitize/validate the href! ⚠️\n      const href = value.href || ''\n\n      if (uriLooksSafe(href)) {\n        const rel = href.startsWith('/') ? undefined : 'noreferrer noopener'\n        return html`\u003ca href=\"${href}\" rel=\"${rel}\"\u003e${children}\u003c/a\u003e`\n      }\n\n      // If the URI appears unsafe, render the children (eg, text) without the link\n      return children\n    },\n  },\n}\n\nconsole.log(toHTML(somePtValue, {components: myPortableTextComponents}))\n```\n\n## Available components\n\nThese are the overridable/implementable keys:\n\n### `types`\n\nAn 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.\n\nUse the `isInline` property to check whether or not this is an inline object or a block.\n\nThe object has the shape `{typeName: ComponentFn}`, where `typeName` is the value set in individual `_type` attributes.\n\n### `marks`\n\nObject 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).\n\nIf 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.\n\nThe component function also receives a `children` property that should (usually) be rendered in whatever parent container makes sense for this mark (eg `\u003ca\u003e`, `\u003cem\u003e`).\n\n### `block`\n\nAn 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).\n\nCan also be set to a single component function, which would handle block styles of _any_ type.\n\n### `list`\n\nObject of component functions used to render lists of different types (`bullet` vs `number`, for instance, which by default is `\u003cul\u003e` and `\u003col\u003e`, respectively).\n\nNote that there is no actual \"list\" node type in the Portable Text specification, but a series of list item blocks with the same `level` and `listItem` properties will be grouped into a virtual one inside of this library.\n\nThe property can also be set to a single component function, which would handle lists of _any_ type.\n\n### `listItem`\n\nObject of component functions used to render different list item styles. The object has the shape `{listItemType: ComponentFn}`, where `listItemType` is the value set in individual `listItem` attributes on blocks.\n\nCan also be set to a single component function, which would handle list items of _any_ type.\n\n### `hardBreak`\n\nComponent function to use for rendering \"hard breaks\", eg `\\n` inside of text spans.\n\nWill by default render a `\u003cbr /\u003e`. Pass `false` to render as-is (`\\n`)\n\n### `unknownMark`\n\nComponent function used when encountering a mark type there is no registered component for in the `components.marks` option.\n\n### `unknownType`\n\nComponent function used when encountering an object type there is no registered component for in the `components.types` option.\n\n### `unknownBlockStyle`\n\nComponent function used when encountering a block style there is no registered component for in the `components.block` option. Only used if `components.block` is an object.\n\n### `unknownList`\n\nComponent function used when encountering a list style there is no registered component for in the `components.list` option. Only used if `components.list` is an object.\n\n### `unknownListItem`\n\nComponent function used when encountering a list item style there is no registered component for in the `components.listItem` option. Only used if `components.listItem` is an object.\n\n## Default components\n\nIf you override the default components but still want access to the original ones, you can access them by importing `defaultComponents`:\n\n```ts\nimport {defaultComponents, toHTML, escapeHTML} from '@portabletext/to-html'\n\ntoHTML(\n  [\n    /* array of portable text blocks */\n  ],\n  {\n    components: {\n      marks: {\n        link: ({children, value, ...rest}) =\u003e {\n          const href = value.href || ''\n          return href.startsWith('https://my.site/')\n            ? `\u003ca href=\"${escapeHTML(href)}\" class=\"internalLink\"\u003e${children}\u003c/a\u003e`\n            : defaultComponents({children, value, ...rest})\n        },\n      },\n    },\n  },\n)\n```\n\n## Disabling warnings / handling unknown types\n\nWhen 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.\n\nTo 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:\n\n```jsx\nimport {toHTML} from '@portabletext/to-html'\n\ntoHTML(\n  [\n    /* array of portable text blocks */\n  ],\n  {onMissingComponent: false},\n)\n\n// or, pass it a function:\n\ntoHTML(\n  [\n    /* array of portable text blocks */\n  ],\n  {\n    onMissingComponent: (message, options) =\u003e {\n      myErrorLogger.report(message, {\n        // eg `someUnknownType`\n        type: options.type,\n\n        // 'block' | 'mark' | 'blockStyle' | 'listStyle' | 'listItemStyle'\n        nodeType: options.nodeType,\n      })\n    },\n  },\n)\n```\n\n## Missing links\n\nIf you find there are links that are not being rendered, it is likely because the `href` property (eg the URI) of the link is not considered \"safe\". This is done to prevent URIs like `javascript:someDangerousFn()` and similar from being rendered. If you want to override this behavior, provide a custom component for the `link` mark:\n\n```js\nimport {escapeHTML} from `@portabletext/to-html`\n\ntoHTML(portableTextBlocks, {\n  components: {\n    marks: {\n      link: ({children, value}) =\u003e {\n        // ⚠️ `value.href` IS NOT \"SAFE\" BY DEFAULT ⚠️\n        // ⚠️ Make sure you sanitize/validate the href! ⚠️\n        const unsafeUri = value.href || ''\n        const looksSafe = /^(http|https|mailto|my-custom-proto):/i.test(unsafeUri)\n        return looksSafe\n          ? `\u003ca href=\"${escapeHTML(value.href)}\"\u003e${children}\u003c/a\u003e`\n          : children\n      },\n    }\n  },\n})\n```\n\n## License\n\nMIT © [Sanity.io](https://www.sanity.io/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fportabletext%2Fto-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fportabletext%2Fto-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fportabletext%2Fto-html/lists"}