Compile HTML templates with prop placeholders into React JSX or JavaScript source at build time. Keep simple layouts in template files, pass data or components through React props, and import the generated component into your application. The compiler and PEG.js parser stay in your build dependencies when you use the generated source.
Use this package for repeatable code generation from simple, trusted templates: reusing a layout with different props, inserting React components into template slots, or converting a collection of controlled templates during a migration.
Choose based on the output you need:
| Need | Tool |
|---|---|
Generate component source with {prop} bindings and <{Component}> slots |
template-to-react |
| Render HTML received from a CMS or API at runtime | html-react-parser or html-to-react |
| Convert ordinary HTML to JSX once | HTMLtoJSX or a converter in your editor |
| Author a new React component directly | JSX/TSX |
This package accepts the template syntax described below. Raw HTML copied from a page may need adaptation, especially styles, boolean attributes, and void tags.
npm install --save-dev template-to-react
# or
pnpm add --save-dev template-to-reactThe package exports ESM and CommonJS entry points and TypeScript declarations. React is used by the generated component and is supplied by your application. Rendering tests cover React 18 and 19.
import { writeFile } from 'node:fs/promises';
import { compileTemplateToReact } from 'template-to-react';
const template = '<div class="{className}">Hello, {name}!</div>';
const code = compileTemplateToReact(template, { componentName: 'Greeting' });
await writeFile(
'./Greeting.jsx',
`import React from 'react';\nexport ${code}\n`,
);
// function Greeting(props){return <div className={props.className}>Hello, {props.name}!</div>}Compile the generated JSX with your application's existing JSX toolchain. For output that is already valid JavaScript, use jsx: true; the generated function references React.Fragment and React.createElement:
const code = compileTemplateToReact(
'<div class="{className}">Hello, {name}!</div>',
{ componentName: 'Greeting', jsx: true, pretty: true },
);
// Save it with `import React from 'react';` and an export, as above.A tag placeholder lets the caller supply a React component:
const code = compileTemplateToReact(
'<section><{Heading}>{title}</{Heading}></section>',
{ componentName: 'Card' },
);
// function Card(props){const C$c0=props.Heading;return <section><C$c0>{props.title}</C$c0></section>}Save and export the generated source as in the first example, then render <Card Heading={YourHeadingComponent} title="Welcome" />. No template parser is needed in the application at render time.
Templates and options must come from trusted sources. Compile during the build and import the result. The compiler is not a sanitizer; avoid evaluating remotely supplied templates. The parser currently generates its grammar at module initialization, which also requires dynamic code generation. Strict browser CSP environments should consume the generated component instead of loading this compiler.
- Use lowercase tags, explicit closing tags, and self-closing void elements:
<div>…</div>,<input/>,<br/>. - Attribute values must be single- or double-quoted. Multiline attributes and whitespace around
=are supported. Bare boolean attributes and unquoted values are not supported; usedisabled="{disabled}"with a boolean prop. {name}references a prop. Text and attribute placeholders also support property keys such as{user-name}and{3className}. They are property names, not JavaScript expressions or nested paths.- Dynamic tags accept identifier placeholders, including
$and_:<{_component}>{children}</{_component}>. The corresponding prop supplies a React component or tag name. Opening and closing tag names must match. - A single placeholder preserves its prop value. Mixed attribute values concatenate into strings; adjacent numeric placeholders such as
{a}{b}produce"12"fora: 1, b: 2. classbecomesclassName, andforbecomeshtmlFor. Supply other React attribute names directly, such astabIndexandreadOnly. Use a prop for object values:style="{style}". CSS declaration strings are not converted into style objects.- Comments are removed. Multiple roots and empty templates use a fragment. Doctypes, implicit tag closing, raw script contents, and general SVG conversion are not supported.
- Entity handling preserves the previous modes: JSX markup decodes HTML entities, while
jsx: truetreats entity spelling as literal text. For example,&renders as&in markup mode and as&in JavaScript mode. Use a prop when its literal value must be identical in both modes. - Leading and trailing whitespace in text nodes is trimmed by default.
reserverWhitespace: truepreserves it in compact output. Pretty printing trims it; the template as a whole is always trimmed.
compileTemplateToReact(template: string, options?: ITemplateToReactOptions): stringReturns component source code, without imports or exports. Parse errors include PEG.js location information; mismatched tag names and invalid component names or indentation throw an error.
interface ITemplateToReactOptions {
// Historical spelling retained for compatibility; defaults to false.
reserverWhitespace?: boolean;
// JavaScript function identifier; defaults to 'TemplateComponent'.
componentName?: string;
// true uses 2 spaces; numeric indentation must be a non-negative integer.
pretty?: boolean | number | { initialIndent: number; indentSize: number };
// false outputs JSX markup; true outputs React.createElement calls.
jsx?: IJsxOptions;
}
type IJsxOptions = boolean | undefined | {
fragment: string;
jsx: string;
jsxs: string;
};The object form of jsx inserts the supplied JavaScript references for a fragment and two factory functions. jsxs handles the root and jsx handles nested elements. Custom factories retain the three-argument (type, attributes, childrenArray) contract, including an empty array for self-closing tags. References to React.createElement use its variadic children contract and omit children for self-closing tags. This is a custom factory contract, not the react/jsx-runtime API. The low-level parser and its AST types are also exported.
Version 1.0.0 includes compiler corrections that can change generated source and rendered output. Rebuild generated components and check your templates before upgrading.
- Public entry points, legacy deep imports, the
reserverWhitespacespelling, JSX entity decoding, and the custom factory argument contract remain available. - In JavaScript output, text placeholders are separate React children. React elements are no longer coerced into strings, and adjacent numbers render independently. In attributes,
{a}{b}concatenates values instead of numerically adding them. - Compact JSX output now preserves actual internal line breaks instead of emitting literal backslash-
ntext. Pretty JSX output retains JSX's whitespace normalization. - Quotes, backslashes, braces, and unsupported identifier characters are escaped correctly. Valid Unicode component names remain supported; invalid function names and indentation fail early.
- Generated source formatting and React factory calls change. Custom factories may receive more children for a text node containing multiple placeholders; they should render the children array rather than assume one entry per original text node.
- Maintainer commands now require pnpm and Node.js 22.12+. This development requirement does not add a Node.js engine restriction to the published library or require consumers to upgrade React.
Use Node.js 22.12+ (22 or 24 LTS) and pnpm 12.8.1.
npm install --global pnpm@12.8.1
pnpm install --frozen-lockfile
pnpm typecheck
pnpm test:coverage
pnpm build
pnpm test:package
pnpm devpnpm test runs once; pnpm test:watch enables watch mode. The package smoke check installs a real tarball into an isolated temporary consumer, then verifies ESM/CommonJS loading and TypeScript declarations. It requires access to the npm registry. prepublishOnly runs the same checks before publication.
The toolchain uses TypeScript 5.9, Vite 8, and Vitest 5. TypeScript 5.9 matches the compiler bundled by the declaration generator and retains the APIs used by the rendering/package checks.
CI checks Node.js 22/24, React 18/19, coverage, the library build, and the packaged entry points. Before contributing a compiler fix, add a regression test that renders the generated component where possible.
The supported template syntax and compiler options are documented above. Maintenance focuses on compiler correctness, compatibility, dependencies, and build reproducibility. Please report unsupported real-world templates with an input example and the expected component output.