react-dom-functions offers a JSX-like API for React, allowing you to build React elements using simple function calls rather than JSX tags. This approach streamlines your code and eliminates the need for a build step or JSX transpilation, making it especially useful for environments where JSX is not available or desired. By providing a set of functions corresponding to standard HTML elements (such as div, span, button, etc.), you can construct your component trees in a more readable and maintainable way compared to using React.createElement directly. This results in cleaner code, improved type safety (especially with TypeScript), and a more ergonomic developer experience when working with React without JSX.
npm install react-dom-functionsNote: This library includes clsx as a dependency for advanced className handling. No additional installation is required.
import React from 'react';
import { div, h1, p, button, DOMFC } from 'react-dom-functions';
type AppProps = {
initialCount?: number;
};
// Use DOMFC instead of React.FC for components that return DOM elements
const App: DOMFC<AppProps> = ({ initialCount = 0 }) => {
const [count, setCount] = React.useState(initialCount);
return div(
{ className: 'app' },
h1('Hello World'),
p('This is a paragraph'),
button(
{
className: {
btn: true,
'btn-primary': true,
'btn-active': count > 0,
},
onClick: () => setCount(count + 1),
},
`Count: ${count}`
)
);
};Instead of using JSX syntax, you can create React elements using function calls:
import { div, h1, p, button, DOMFC } from 'react-dom-functions';
type MyComponentProps = {
title: string;
description: string;
};
// Use DOMFC instead of React.FC for components that return DOM elements
const MyComponent: DOMFC<MyComponentProps> = ({ title, description }) => {
return div(
{ className: 'container' },
h1({ className: 'title' }, title),
p({ className: 'description' }, description),
button(
{
className: 'btn',
onClick: () => console.log('clicked'),
},
'Click me'
)
);
};While JSX and TSX are popular for React development, using pure TypeScript with function calls offers several advantages:
- Better IntelliSense: Function calls provide more precise autocomplete and type checking compared to JSX syntax
- Compile-time Validation: TypeScript can catch more errors at compile time with explicit function signatures
- No JSX Transform Dependencies: Eliminates the need for JSX transformation in your build pipeline
- Smaller Bundle Size: No JSX runtime overhead or transformation costs
- Faster Compilation: Direct function calls compile faster than JSX transformation
- Tree-shaking Friendly: Function imports can be better optimized by bundlers
- Consistent Syntax: Uses familiar function call patterns instead of HTML-like syntax
- Better Refactoring: IDEs can more easily refactor function calls than JSX elements
- Explicit Dependencies: Clear import statements make dependencies more visible
- Simpler Configuration: No need to configure JSX transformation in your build tools
- Framework Agnostic: Works with any build system that supports TypeScript
- Reduced Tooling Complexity: Fewer dependencies and configuration requirements
- Programmatic Creation: Easier to generate elements programmatically
- Dynamic Components: More straightforward to create components based on data or conditions
- Template Systems: Better integration with template engines and code generation tools
The DOMFC type is a specialized React function component type designed for components that return DOM elements using the function-based API. Use DOMFC wherever you would normally use React.FC when building components with this library. It provides better type safety and IntelliSense support compared to the standard React.FC.
import { div, span, DOMFC } from 'react-dom-functions';
type CardProps = {
title: string;
content: string;
variant?: 'primary' | 'secondary';
};
// DOMFC replaces React.FC for function-based DOM components
const Card: DOMFC<CardProps> = ({ title, content, variant = 'primary' }) => {
return div(
{
className: {
card: true,
[`card--${variant}`]: true,
},
},
span({ className: 'card__title' }, title),
span({ className: 'card__content' }, content)
);
};The DOMFC type automatically includes support for children:
import { div, DOMFC } from 'react-dom-functions';
type ContainerProps = {
padding?: 'small' | 'medium' | 'large';
};
// Use DOMFC instead of React.FC for better children support
const Container: DOMFC<ContainerProps> = ({ padding = 'medium', children }) => {
return div(
{
className: {
container: true,
[`container--${padding}`]: true,
},
},
children
);
};import { div, button, span, DOMFC } from 'react-dom-functions';
type ButtonProps = {
variant: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
onClick?: () => void;
};
// DOMFC provides better type safety than React.FC for DOM components
const Button: DOMFC<ButtonProps> = ({
variant,
size = 'medium',
disabled = false,
onClick,
children,
}) => {
return button(
{
className: {
btn: true,
[`btn--${variant}`]: true,
[`btn--${size}`]: true,
'btn--disabled': disabled,
},
disabled,
onClick: disabled ? undefined : onClick,
},
children
);
};Replace React.FC with DOMFC in the following scenarios:
- Function Components: Any component that returns DOM elements using this library
- Type Safety: When you want better type checking for DOM element returns
- Consistent API: To maintain consistency with the function-based approach
- Children Support: When your component needs to handle children properly
// ❌ Don't use React.FC with this library
const MyComponent: React.FC<MyProps> = ({ title }) => {
return div({ className: 'container' }, title);
};
// âś… Use DOMFC instead
const MyComponent: DOMFC<MyProps> = ({ title }) => {
return div({ className: 'container' }, title);
};Key Differences:
DOMFCis specifically designed for components that return DOM elementsDOMFCprovides better type safety for the function-based APIDOMFCautomatically includes proper children typingDOMFCmatches the library's function-based approach
- Type Safety: Ensures your component returns a DOM element
- Better IntelliSense: Provides accurate autocomplete for props and children
- Consistent API: Matches the function-based approach of the library
- Children Support: Automatically includes proper typing for children
- IDE Integration: Better refactoring and error detection
- React.FC Replacement: Drop-in replacement for React.FC with enhanced DOM support
All HTML elements are available as functions. Each function accepts:
- Props object (optional): An object containing React props
- Children (optional): React nodes as additional arguments
import { div, span, p, h1, h2, h3, h4, h5, h6 } from 'react-html-elements';
// With props and children
div({ className: 'container' }, 'Hello World');
// With multiple children
div(
{ className: 'container' },
h1({ className: 'title' }, 'Title'),
p({ className: 'text' }, 'Paragraph')
);
// Without props
div('Just text content');
// With multiple children without props
div(span('First child'), span('Second child'));The className prop supports clsx syntax for conditional and dynamic class names. This allows you to create complex class combinations easily:
import { div, button, span } from 'react-dom-functions';
function DynamicComponent({ isActive, size, variant }) {
return div(
{
className: {
'base-class': true,
active: isActive,
disabled: !isActive,
[`size-${size}`]: size,
[`variant-${variant}`]: variant,
},
},
button(
{
className: [
'btn',
'btn-primary',
isActive && 'btn-active',
size && `btn-${size}`,
],
},
'Click me'
),
span(
{
className: clsx(
'status',
isActive ? 'status-active' : 'status-inactive',
size && `status-${size}`
),
},
isActive ? 'Active' : 'Inactive'
)
);
}Conditional classes:
div(
{
className: {
base: true,
active: isActive,
disabled: isDisabled,
},
},
'Content'
);Array syntax:
div(
{
className: [
'base-class',
isActive && 'active-class',
size && `size-${size}`,
],
},
'Content'
);Direct clsx function:
import clsx from 'clsx';
div(
{
className: clsx(
'base',
isActive && 'active',
size && `size-${size}`,
variant && `variant-${variant}`
),
},
'Content'
);Mixed syntax:
div(
{
className: [
'base',
{ conditional: someCondition },
otherCondition && 'other-class',
],
},
'Content'
);Use the fragment function to create React fragments:
import { fragment, div, span } from 'react-dom-functions';
function MyComponent() {
return fragment(div('First element'), div('Second element'));
}import {
form,
input,
button,
label,
textarea,
select,
option,
div,
DOMFC,
} from 'react-dom-functions';
type ContactFormProps = {
onSubmit?: (data: { name: string; message: string }) => void;
};
const ContactForm: DOMFC<ContactFormProps> = ({ onSubmit }) => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
onSubmit?.({
name: formData.get('name') as string,
message: formData.get('message') as string,
});
};
return form(
{ onSubmit: handleSubmit },
div(
{ className: 'form-group' },
label({ htmlFor: 'name' }, 'Name:'),
input({
id: 'name',
name: 'name',
type: 'text',
placeholder: 'Enter your name',
})
),
div(
{ className: 'form-group' },
label({ htmlFor: 'message' }, 'Message:'),
textarea({
id: 'message',
name: 'message',
rows: 4,
placeholder: 'Enter your message',
})
),
button({ type: 'submit' }, 'Submit')
);
};import { ul, ol, li, DOMFC } from 'react-dom-functions';
type TodoListProps = {
todos: string[];
onTodoClick?: (todo: string, index: number) => void;
};
const TodoList: DOMFC<TodoListProps> = ({ todos, onTodoClick }) => {
return ul(
{ className: 'todo-list' },
...todos.map((todo, index) =>
li(
{
key: index,
className: 'todo-item',
onClick: () => onTodoClick?.(todo, index),
},
todo
)
)
);
};import { table, thead, tbody, tr, th, td, DOMFC } from 'react-dom-functions';
type Person = {
name: string;
age: number;
};
type DataTableProps = {
data: Person[];
onRowClick?: (person: Person, index: number) => void;
};
const DataTable: DOMFC<DataTableProps> = ({ data, onRowClick }) => {
return table(
{ className: 'data-table' },
thead(tr(th('Name'), th('Age'))),
tbody(
...data.map((row, index) =>
tr(
{
key: index,
onClick: () => onRowClick?.(row, index),
className: 'table-row',
},
td(row.name),
td(row.age.toString())
)
)
)
);
};import { svg, circle, rect, path, DOMFC } from 'react-dom-functions';
type SimpleIconProps = {
size?: number;
color?: string;
};
const SimpleIcon: DOMFC<SimpleIconProps> = ({ size = 100, color = 'blue' }) => {
return svg(
{ width: size, height: size, viewBox: `0 0 ${size} ${size}` },
circle({
cx: size / 2,
cy: size / 2,
r: size * 0.4,
fill: color,
})
);
};title,base,link,meta,style
address,article,aside,footer,headerh1,h2,h3,h4,h5,h6,hgroupmain,nav,section
blockquote,dd,div,dl,dtfigcaption,figure,hr,li,ol,p,pre,ul
a,abbr,b,bdi,bdo,br,cite,codedata,dfn,em,i,kbd,mark,qrb,rp,rt,rtc,ruby,s,sampsmall,span,strong,sub,sup,timeu,var_,wbr
area,audio,img,map,track,video
embed,iframe,object,param,picture,source
canvas,noscript,script
del,ins
caption,col,colgroup,table,tbodytd,tfoot,th,thead,tr
button,datalist,fieldset,form,inputlabel,legend,meter,optgroup,optionoutput,progress,select,textarea
details,dialog,menu,menuitem,summary
slot,template
svg,circle,rect,path,line,polygonpolyline,ellipse,g,text,tspan
math,mrow,mfrac,msqrt,mroot,msubmsup,msubsup,munder,mover,munderovermmultiscripts,mtable,mtr,mtd,mactionmerror,mpadded,mphantom,mspace,mstylems,mtext,mn,mo,mi
This library is written in TypeScript and provides full type safety. All element functions are properly typed with React's element types.
Important: When creating function components with this library, use DOMFC instead of React.FC for better type safety and consistency with the function-based API.
This library is optimized for performance:
- Tree-shaking friendly: Only import what you use
- Minimal runtime overhead: Direct function calls
- Memoized element creation: Cached element functions
- Small bundle size: ~2KB gzipped
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT