dangerously-set-html-content

A tiny React component that renders raw HTML strings and actually executes any inline or external script tags inside them.

Library
npm
v1.1.1
62stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
34/100Needs Attention
Development Activity4
Maintenance20
Community36
Maturity56
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
61/100Good
Architecture55
Code Quality60
Innovation75
Learning Curve55

React’s built-in dangerouslySetInnerHTML prop deliberately refuses to execute <script> tags found in the HTML string it renders, because it sets content via the DOM’s innerHTML property, which the browser treats as inert markup for safety reasons. dangerously-set-html-content works around that limitation by using the DOM Range API’s createContextualFragment method to parse the HTML string into a real document fragment, which the browser does execute scripts for, then injects that fragment into a ref-attached container div.

The component is a single, dependency-free (aside from a React peer dependency) function component with two props: html (the string to render) and an optional allowRerender boolean that controls whether subsequent prop changes re-inject content after the first render. It is intentionally narrow in scope, README and typings included, aimed at cases like injecting third-party embed snippets, ad tags, or CMS-authored HTML blocks that must run their own scripts.

What You Get

  • A single <InnerHTML html={...} /> component with no runtime dependencies beyond React
  • Script execution support for both inline <script> blocks and external <script src=...> tags
  • An allowRerender prop to opt into re-injecting content when the html string changes after first mount
  • Bundled TypeScript type declarations (index.d.ts) for the component’s props
  • Pass-through of any other props (e.g. className, id) directly onto the rendered container div

Common Use Cases

  • Rendering third-party ad or tracking snippets that include their own <script> tags
  • Embedding CMS- or marketing-authored HTML blocks (landing page builders, email templates) that need inline scripts to run
  • Injecting widget/embed code (chat widgets, payment buttons, social embeds) supplied as raw HTML+JS strings
  • Any case where dangerouslySetInnerHTML silently strips required script behavior

Under The Hood

Architecture The entire implementation lives in one file, src/index.js — a single function component with no internal module boundaries to speak of. It holds two refs (divRef for the container DOM node and isFirstRender as a mutable guard) and one useEffect that runs whenever html or divRef change. Inside that effect it early-returns if a rerender isn’t allowed and hasn’t been explicitly opted into via allowRerender, otherwise it builds a document fragment from the HTML string and swaps it into the container. There’s no state management or composition to reason about; the entire contract is the two props it accepts and the div it renders via createElement, so a consumer’s mental model of what breaks if this changes reduces to that one effect body.

Tech Stack The package has a single peer dependency, react (>=18.2.0), and ships plain CommonJS (require/module.exports) rather than ES modules or a bundler-produced dual build. Its dev/build tooling is Create React App’s react-scripts for running tests under jsdom, eslint with the standard + standard-react configs plus eslint-plugin-prettier for lint/format enforcement, and npm-run-all/cross-env to sequence the test:unit/test:lint scripts. Type consumers get a hand-written index.d.ts ambient module declaration rather than being compiled from TypeScript source.

Code Quality The component is covered by a focused Jest/@testing-library/react test suite (src/index.test.js) exercising six scenarios: rejecting an empty html prop, rendering plain markup, rendering markup with an executing inline script, confirming pass-through props land on the container, and both the allowRerender and no-rerender update paths. Error handling is a single explicit throw when html is missing rather than a silent no-op. There is no CI workflow file in the repository, so test/lint execution on push isn’t automatically enforced, and the source itself is untyped JavaScript with types maintained by hand in a separate .d.ts file rather than derived from the implementation.

API Design The public surface is deliberately tiny: one component, two meaningful props (html, allowRerender), with any additional props forwarded straight to the rendered div — so there’s effectively zero boilerplate to adopt it beyond import and a single JSX tag. The README documents both props in a table and calls out the rerender caveat explicitly with a link to the PR that introduced it, which is unusually good honesty about a non-obvious default (first-render-only injection) that could otherwise surprise consumers.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search