{"_id":"@charlietango/umbraco-rich-text","_rev":"3-b532dbd60d70679d17b0b98b60ad5d28","name":"@charlietango/umbraco-rich-text","dist-tags":{"latest":"3.0.0"},"versions":{"1.0.0":{"name":"@charlietango/umbraco-rich-text","version":"1.0.0","keywords":["umbraco","react","rich text","content delivery api","headless cms"],"author":{"name":"Charlie Tango"},"license":"MIT","_id":"@charlietango/umbraco-rich-text@1.0.0","maintainers":[{"name":"thebuilder","email":"dsc@charlietango.dk"},{"name":"frederik_bosch","email":"frederik@bosch.dev"}],"homepage":"https://github.com/charlie-tango/umbraco-rich-text#readme","bugs":{"url":"https://github.com/charlie-tango/umbraco-rich-text/issues"},"dist":{"shasum":"c3761a2e29d5828fd3e02944bb1ccb2877b58cea","tarball":"https://registry.npmjs.org/@charlietango/umbraco-rich-text/-/umbraco-rich-text-1.0.0.tgz","fileCount":8,"integrity":"sha512-m4ku55ctrNJG3SvymON6p0ez0leWfUJGJjyi4OzUDct2VmyNoNuMLoX0/vRxL7A2I39OJ1jM/HX30G2KNu15iw==","signatures":[{"sig":"MEUCIER7OnLJCuOVSALRl3MAzm8LwkhK9wGQbbZIwE7XA4U2AiEAwwp1ujTqgwufvKXK8Qy4J7ZXii61b3D4BzsDQP0F0wE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45107},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"gitHead":"f16ce280812810c35e6002a25fc7b17c341d06bb","private":false,"scripts":{"dev":"unbuild --stub","lint":"biome check .","test":"vitest","build":"unbuild","prepare":"simple-git-hooks","release":"bumpp && npm publish","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"thebuilder","email":"dsc@charlietango.dk"},"prettier":{"proseWrap":"always","printWidth":80},"repository":{"url":"git+https://github.com/charlie-tango/umbraco-rich-text.git","type":"git"},"stackblitz":{"startCommand":"pnpm build && pnpm --filter=umbraco-rich-text-example dev"},"_npmVersion":"10.8.2","description":"React component for working with rich text from the Umbraco Content Delivery API.","directories":{},"lint-staged":{"*.md":["prettier --write"],"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --fix --no-errors-on-unmatched"]},"sideEffects":false,"_nodeVersion":"22.18.0","dependencies":{"html-entities":"^2.6.0"},"typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748","devDependencies":{"vite":"^7.1.2","bumpp":"^10.2.3","react":"^19.1.1","rimraf":"^6.0.1","vitest":"^3.2.4","unbuild":"^3.6.0","prettier":"^3.6.2","react-dom":"^19.1.1","playwright":"^1.54.2","typescript":"^5.9.2","@types/node":"^24.2.1","lint-staged":"^16.1.5","@types/react":"^19.1.10","@biomejs/biome":"^2.1.4","@vitest/browser":"^3.2.4","@types/react-dom":"^19.1.7","simple-git-hooks":"^2.13.1","@vitejs/plugin-react":"^5.0.0","vitest-browser-react":"^1.0.1"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged"},"_npmOperationalInternal":{"tmp":"tmp/umbraco-rich-text_1.0.0_1755180858762_0.5019007143274488","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@charlietango/umbraco-rich-text","version":"2.0.0","keywords":["umbraco","react","rich text","content delivery api","headless cms"],"author":{"name":"Charlie Tango"},"license":"MIT","_id":"@charlietango/umbraco-rich-text@2.0.0","maintainers":[{"name":"thebuilder","email":"dsc@charlietango.dk"},{"name":"frederik_bosch","email":"frederik@bosch.dev"}],"homepage":"https://github.com/charlie-tango/umbraco-rich-text#readme","bugs":{"url":"https://github.com/charlie-tango/umbraco-rich-text/issues"},"dist":{"shasum":"982517c65da7f4af352ba22e81adb3ad6735d7ba","tarball":"https://registry.npmjs.org/@charlietango/umbraco-rich-text/-/umbraco-rich-text-2.0.0.tgz","fileCount":8,"integrity":"sha512-avkYwBMhV9EY/h4X6cCEgFVQtolQt8dfJtWWNj+rJh1c4l+5sAnLGlncIJOUmz2Zrth5l/7L0ldoxvgGgpUnuA==","signatures":[{"sig":"MEUCIFffvjspnAETEXCwPavGMmXPm0sfYm7XICXPLlE0mDdxAiEAuvUZ48fsoXaelF6NirO7HC7EX4Zhjr/62lA+Af/6neo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50643},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"gitHead":"528bfbe3cffc7e973278e05e79ee410685a5dfd6","private":false,"scripts":{"dev":"unbuild --stub","lint":"biome check .","test":"vitest","build":"unbuild","prepare":"simple-git-hooks","release":"bumpp && npm publish","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"thebuilder","email":"dsc@charlietango.dk"},"prettier":{"proseWrap":"always","printWidth":80},"repository":{"url":"git+https://github.com/charlie-tango/umbraco-rich-text.git","type":"git"},"stackblitz":{"startCommand":"pnpm build && pnpm --filter=@charlietango/umbraco-rich-text--example dev"},"_npmVersion":"11.6.2","description":"React component for working with rich text from the Umbraco Content Delivery API.","directories":{},"lint-staged":{"*.md":["prettier --write"],"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --fix --no-errors-on-unmatched"]},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"html-entities":"^2.6.0"},"typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}},"_hasShrinkwrap":false,"packageManager":"pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748","devDependencies":{"vite":"^7.1.2","bumpp":"^10.3.2","react":"^19.2.3","rimraf":"^6.1.2","vitest":"^3.2.4","unbuild":"^3.6.1","prettier":"^3.7.4","react-dom":"^19.2.3","playwright":"^1.57.0","typescript":"^5.9.3","@types/node":"^24.2.1","lint-staged":"^16.2.7","@types/react":"^19.2.7","@biomejs/biome":"^2.3.8","@vitest/browser":"^3.2.4","@types/react-dom":"^19.2.3","simple-git-hooks":"^2.13.1","@vitejs/plugin-react":"^5.0.0","vitest-browser-react":"^1.0.1"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged"},"_npmOperationalInternal":{"tmp":"tmp/umbraco-rich-text_2.0.0_1765547739726_0.8493013159389537","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@charlietango/umbraco-rich-text","private":false,"type":"module","version":"3.0.0","packageManager":"pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748","description":"React component for working with rich text from the Umbraco Content Delivery API.","author":{"name":"Charlie Tango"},"license":"MIT","homepage":"https://github.com/charlie-tango/umbraco-rich-text#readme","repository":{"type":"git","url":"git+https://github.com/charlie-tango/umbraco-rich-text.git"},"bugs":{"url":"https://github.com/charlie-tango/umbraco-rich-text/issues"},"keywords":["umbraco","react","rich text","content delivery api","headless cms"],"sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"main":"./dist/index.mjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","typesVersions":{"*":{"*":["./dist/*","./dist/index.d.ts"]}},"scripts":{"build":"unbuild","dev":"unbuild --stub","lint":"biome check .","prepublishOnly":"pnpm build","test":"vitest","typecheck":"tsc --noEmit","prepare":"simple-git-hooks"},"stackblitz":{"startCommand":"pnpm build && pnpm --filter=@charlietango/umbraco-rich-text--example dev"},"dependencies":{"html-entities":"^2.6.0"},"peerDependencies":{"react":"^18.0.0 || ^19.0.0"},"devDependencies":{"@biomejs/biome":"^2.3.8","@types/node":"^24.2.1","@types/react":"^19.2.7","@types/react-dom":"^19.2.3","@vitejs/plugin-react":"^5.0.0","@vitest/browser":"^3.2.4","bumpp":"^10.3.2","lint-staged":"^16.2.7","playwright":"^1.57.0","prettier":"^3.7.4","react":"^19.2.3","react-dom":"^19.2.3","rimraf":"^6.1.2","simple-git-hooks":"^2.13.1","typescript":"^5.9.3","unbuild":"^3.6.1","vite":"^7.1.2","vitest":"^3.2.4","vitest-browser-react":"^1.0.1"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged"},"lint-staged":{"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --fix --no-errors-on-unmatched"],"*.md":["prettier --write"]},"prettier":{"proseWrap":"always","printWidth":80},"gitHead":"f65575630d2ef2e19f4179a34349d4d3fbdcc747","_id":"@charlietango/umbraco-rich-text@3.0.0","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-dQ/sQcmlybZP+qrsiDETQGhcZTFhQOZKZLtDdB37vQ20JBoHEPkm7RzdUlz2hcw043/ybdaA67l7ReBL9WjTpQ==","shasum":"d1e20bec887b5169fe4c4f2c129745ffe566f4ff","tarball":"https://registry.npmjs.org/@charlietango/umbraco-rich-text/-/umbraco-rich-text-3.0.0.tgz","fileCount":8,"unpackedSize":61223,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@charlietango%2fumbraco-rich-text@3.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAhw/xFNJeUUSssL3LRhJWTGLaaz2jqs/2iKiSwfXiC9AiEAzlDRMFJSNFvZM6wtXwzqq8OCzn11Cy9OMj2IlItgttk="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a38a1825-5f57-4140-9777-833b7f90e535"}},"directories":{},"maintainers":[{"name":"thebuilder","email":"dsc@charlietango.dk"},{"name":"frederik_bosch","email":"frederik@bosch.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/umbraco-rich-text_3.0.0_1787920962179_0.32118424211425034"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-14T14:14:18.693Z","modified":"2026-08-28T12:42:42.629Z","1.0.0":"2025-08-14T14:14:18.937Z","2.0.0":"2025-12-12T13:55:39.879Z","3.0.0":"2026-08-28T12:42:42.327Z"},"bugs":{"url":"https://github.com/charlie-tango/umbraco-rich-text/issues"},"author":{"name":"Charlie Tango"},"license":"MIT","homepage":"https://github.com/charlie-tango/umbraco-rich-text#readme","keywords":["umbraco","react","rich text","content delivery api","headless cms"],"repository":{"type":"git","url":"git+https://github.com/charlie-tango/umbraco-rich-text.git"},"description":"React component for working with rich text from the Umbraco Content Delivery API.","maintainers":[{"name":"thebuilder","email":"dsc@charlietango.dk"},{"name":"frederik_bosch","email":"frederik@bosch.dev"}],"readme":"# Umbraco Rich Text\n\n[![npm version][npm-version-src]][npm-version-href]\n[![License][license-src]][license-href]\n\nTakes the JSON rich text output from the\n[Umbraco Content Delivery API](https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api)\nand renders it with React.\n\n> [!IMPORTANT]  \n> You need to enable the `RichTextOutputAsJson` option in the Content Delivery\n> API. See\n> [Content Delivery API configuration](https://docs.umbraco.com/umbraco-cms/reference/content-delivery-api#additional-configuration)\n> for details.\n\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/charlie-tango/umbraco-rich-text/tree/main?file=examples/UmbracoRichText/src/RichText.tsx)\n\n## Install\n\nInstall the `@charlietango/umbraco-rich-text` package with your package manager\nof choice.\n\n```sh\nnpm install @charlietango/umbraco-rich-text\n```\n\n## `<UmbracoRichText>`\n\n### Props\n\n- `element`: The rich text property from the Umbraco Content Delivery API.\n- `renderBlock`: Render a specific block type. Return `null` to skip rendering\n  the node.\n- `renderNode`: Overwrite the default rendering of a node. Return `undefined` to\n  render the default node. Return `null` to skip rendering the node.\n  - `meta`: A function you can call to retrieve metadata describing the current\n    element’s `ancestor`, `children`, `previous`, and `next` siblings. The\n    values are built lazily when you invoke the function, keeping memory lower\n    when `renderNode` doesn’t need the metadata for a given node.\n- `htmlAttributes`: Default attributes to set on the defined HTML elements.\n  These will be used, unless the element already has the attribute set. The only\n  exception is the `className` attribute, which will be merged with the default\n  value.\n- `stripStyles`: Remove inline style attributes from HTML elements. Accepts:\n  - `true`: Removes all inline styles from all HTML elements\n  - An object with configuration options:\n    - `tags`: Array of HTML tags from which to strip styles. If not provided,\n      styles are stripped from all tags.\n    - `except`: Array of HTML tags that should keep their styles, even if they\n      are in the `tags` array.\n  - Default is `false` (all inline styles are preserved).\n- `sanitize`: Opt-in sanitization of CMS-authored content. Both options are\n  independently optional, and omitting `sanitize` (the default) changes nothing.\n  Accepts an object with:\n  - `allowedHrefSchemes`: Allowed URL schemes for `<a href>`, compared\n    case-insensitively without the trailing colon (e.g.\n    `[\"http\", \"https\", \"mailto\"]`). Hrefs with no scheme (relative paths,\n    `#hash`, `?query`, protocol-relative `//host`) always pass. When a scheme is\n    present and not in the list, the `href` attribute is removed; the anchor\n    element and its other attributes still render.\n  - `stripAttributes`: Strip attributes by name, after HTML-to-React mapping.\n    Accepts:\n    - `true`: Uses the default unsafe list: event handlers (`/^on[a-z]/i`),\n      `formaction`, and `dangerouslySetInnerHTML`.\n    - An array of `string | RegExp`: Replaces the default list entirely. Strings\n      match case-insensitively, and `RegExp`s are tested against the mapped\n      attribute name.\n  - Default is `undefined` (nothing is sanitized).\n\nWhen passing the `renderBlock` and `renderNode` props, consider making them\nstatic functions (move them outside the consuming component) to avoid\nunnecessary re-renders.\n\n```tsx\nimport {\n  UmbracoRichText,\n  RenderBlockContext,\n  RenderNodeContext,\n} from \"@charlietango/umbraco-rich-text\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nfunction renderNode({ tag, children, attributes }: RenderNodeContext) {\n  switch (tag) {\n    case \"a\":\n      return <Link {...attributes}>{children}</Link>;\n    case \"p\":\n      return (\n        <p className=\"text-lg\" {...attributes}>\n          {children}\n        </p>\n      );\n    default:\n      // Return `undefined` to render the default HTML node\n      return undefined;\n  }\n}\n\nfunction renderBlock({ content }: RenderBlockContext) {\n  switch (content?.contentType) {\n    // Switch over your Umbraco document types that can be rendered in the Rich Text blocks\n    case \"imageBlock\":\n      return <Image {...content.properties} />;\n    default:\n      return null;\n  }\n}\n\nfunction RichText({ data }) {\n  return (\n    <UmbracoRichText\n      data={data.richText}\n      renderNode={renderNode}\n      renderBlock={renderBlock}\n      htmlAttributes={{ p: { className: \"mb-4\" } }}\n      stripStyles={{\n        // Strip styles from all tags except the following:\n        except: [\"img\"], // Keep styles on `img` tags\n      }}\n    />\n  );\n}\n```\n\n### `renderNode` metadata\n\nUse the `meta` argument in `renderNode` to inspect the current node’s context,\nlike its children, siblings, or ancestors. Call `meta()` to build the metadata\nfor the current node only when you need it. This can be helpful for removing\nwrappers you don’t need when rendering. For example, you can drop the paragraph\ntag around blocks so they render directly:\n\n```tsx\nfunction renderNode({ tag, children, attributes, meta }: RenderNodeContext) {\n  switch (tag) {\n    case \"p\": {\n      if (!children) return null;\n      const blockTags = [\"umb-rte-block-inline\", \"umb-rte-block\"];\n      const pChildren = meta().children ?? [];\n      if (\n        pChildren.length === 1 &&\n        blockTags.includes(pChildren[0]?.tag ?? \"\")\n      ) {\n        // If the paragraph only contains a block or inline block,\n        // do not include it in a paragraph tag.\n        return children;\n      }\n\n      return (\n        <p {...attributes} className=\"body-md\">\n          {children}\n        </p>\n      );\n    }\n    default:\n      return undefined;\n  }\n}\n```\n\n### Blocks\n\nYou can augment the `renderBlock` method with the generated OpenAPI types from\nUmbraco Content Delivery API. That way you can correctly filter the blocks you\nare rendering, based on the `contentType`, and get the associated `properties`.\nCreate `types/umbraco-rich-text.d.ts`, and augment the `UmbracoBlockItemModel`\ninterface with your applications definition for `ApiBlockItemModel`.\n\nTo generate the types, you'll want to use the\n[Delivery Api Extensions](https://marketplace.umbraco.com/package/umbraco.community.deliveryapiextensions)\npackage, alongside a tool to generate the types from the OpenAPI schema, like\n[openapi-typescript](https://openapi-ts.dev/).\n\n**types/umbraco-rich-text.d.ts**\n\n```ts\n// Import the `components` generated by OpenAPI TypeScript.\nimport { components } from \"./umbraco-openapi\";\n\n// Define the intermediate interface\ntype ApiBlockItemModel = components[\"schemas\"][\"ApiBlockItemModel\"];\n\ndeclare module \"@charlietango/umbraco-rich-text\" {\n  interface UmbracoBlockItemModel extends ApiBlockItemModel {}\n}\n```\n\n## `richTextToPlainText`\n\nA utility function to convert an Umbraco RichText element to plain text. This\ncan be useful for generating meta descriptions or other text-based properties.\n\n### Parameters\n\n- `data` (`RichTextElementModel`): The rich text element to be converted.\n- `options` (`Options`, _optional_): An object to specify additional options.\n  - `firstParagraph` (`boolean`, _optional_): If `true`, only the first\n    paragraph with text content will be returned.\n  - `maxLength` (`number`, _optional_): The maximum length of the returned text.\n    If the text exceeds this length, it will be truncated to the nearest word\n    and an ellipsis will be added. The returned string may be up to 3 characters\n    longer than `maxLength` because the ellipsis is appended after truncation.\n  - `ignoreTags` (`Array<string>`, _optional_): An array of tags to be ignored\n    during the conversion.\n\n### Returns\n\n- `string`: The plain text representation of the rich text element.\n\n### Example\n\n```ts\nimport { richTextToPlainText } from \"@charlietango/umbraco-rich-text\";\n\nconst plainText = richTextToPlainText(richTextData);\n\n// Just the first paragraph\nconst firstParagraph = richTextToPlainText(richTextData, {\n  firstParagraph: true,\n});\n\n// Just the first 100 characters, truncated at the nearest word with an ellipsis\nconst first100Characters = richTextToPlainText(richTextData, {\n  maxLength: 100,\n});\n\n// Ignore certain tags, skipping their content\nconst ignoreTags = richTextToPlainText(richTextData, {\n  ignoreTags: [\"h1\", \"h2\", \"ol\", \"figure\"],\n});\n```\n\n## Tips & Tricks\n\n### Passing values to `renderNode` and `renderBlock`\n\nYou can pass additional values to the `renderNode` and `renderBlock` functions,\nby making an inline function that returns the `renderNode` or `renderBlock`\nfunction. This can be useful if you need to pass extra context or props to the\nrendering functions. E.g. `sizes` for images, translations, or other data that\nis not part of the rich text element.\n\n```tsx\nimport {\n  UmbracoRichText,\n  RenderBlockContext,\n  RenderNodeContext,\n} from \"@charlietango/umbraco-rich-text\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nfunction renderNode(\n  { tag, children, attributes }: RenderNodeContext,\n  extra: { sizes: string },\n) {\n  switch (tag) {\n    case \"img\":\n      return <img {...attributes} sizes={extra.sizes} />;\n    default:\n      return undefined;\n  }\n}\n\nfunction RichText({ data }) {\n  return (\n    <UmbracoRichText\n      data={data.richText}\n      renderNode={(node) => {\n        return renderNode(node, { sizes: \"720vw\" });\n      }}\n    />\n  );\n}\n```\n\n<!-- Badges -->\n\n[npm-version-src]:\n  https://img.shields.io/npm/v/@charlietango/umbraco-rich-text?style=flat&colorA=080f12&colorB=1fa669\n[npm-version-href]: https://npmjs.com/package/@charlietango/umbraco-rich-text\n[license-src]:\n  https://img.shields.io/github/license/charlie-tango/umbraco-rich-text.svg?style=flat&colorA=080f12&colorB=1fa669\n[license-href]:\n  https://github.com/charlie-tango/umbraco-rich-text/blob/main/LICENSE\n","readmeFilename":"README.md"}