JSX Runtime
AtomJS ships its own JSX runtime so that JSX written inside your class components compiles into AtomJS VNodes, not anything else. You don’t import the runtime manually—TypeScript routes JSX calls to AtomJS based on your tsconfig settings.
Configure TypeScript
Enable the modern JSX transform and point it at AtomJS:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@atomdev/core"
}
}
jsx: "react-jsx"enables TypeScript’s automatic JSX transform (no pragma comments needed).jsxImportSource: "@atomdev/core"tells TS to emit calls to AtomJS’sjsx,jsxs, andjsxDEV.
We plan to ship an official AtomJS tsconfig preset (e.g., @atomdev/tsconfig) so new projects can extend a base config with JSX pre-wired to AtomJS.
Until then, using jsx: "react-jsx" is just a TypeScript flag name—it does not add React or tie you to it. The emitted calls still target @atomdev/core.
You don’t need to import
jsx/jsxs/jsxDEVyourself. The compiler injects those calls.
How JSX becomes VNodes
When you write JSX in a component’s render():
import { AtomComponent } from '@atomdev/core';
export class Hello extends AtomComponent<{ name: string }> {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
TypeScript compiles it roughly to:
// Pseudocode showing the idea:
jsx('h1', { children: ['Hello, ', this.props.name] });
- Intrinsic elements like
<div>or<button>produce VNodes with a stringtype. - Component elements like
<Hello />produce VNodes whosetypeis your class (extendingAtomComponent).
jsx, jsxs, and jsxDEV
jsx: emitted for elements with one child.jsxs: emitted for elements with multiple children.jsxDEV: emitted in development builds; includes helpful metadata such as__sourceand__selfto improve diagnostics.
You don’t call these directly—the compiler does.
Props, Children, and Keys
export class List extends AtomComponent<{ items: { id: string; label: string }[] }> {
render() {
return (
<ul>
{this.props.items.map((item) => (
<li key={item.id}>{item.label}</li>
))}
</ul>
);
}
}
childrenis passed automatically for nested JSX.keyis a special prop used to reconcile lists efficiently. Prefer a stable ID over an array index.
Fragments
export class Duo extends AtomComponent {
render() {
return (
<>
<span>Alpha</span>
<span>Beta</span>
</>
);
}
}
Fragments compile to VNodes that do not create a real DOM node.
Events in JSX
export class Button extends AtomComponent<{ label?: string }> {
handleClick = () => {
// ...
};
render() {
return <button onClick={this.handleClick}>{this.props.label ?? 'Click'}</button>;
}
}
(See the Events page for patterns and guardrails around state updates.)
Type Safety
AtomJS provides typings so that:
- Intrinsic elements validate attributes in JSX.
- Component props are strongly typed via generics on
AtomComponent<P, S>. - Children are typed through the
childrenprop onPwhen needed.
If you maintain your own global JSX declarations, ensure they’re compatible with AtomJS’s VNode/Props types.
Common Pitfalls
- Missing
jsxImportSource→ JSX won’t resolve to AtomJS; double-checktsconfig.json. - Using function components → AtomJS is class-based; extend
AtomComponentand implementrender(). - Calling
setStatein the constructor → Not allowed; initialize withthis.state = { … }in the constructor and usesetStateonly after mount.
Go deeper with Element Creation (createElement) to understand the lower-level building blocks behind the JSX runtime.