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/jsxDEV
yourself. 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 whosetype
is 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__source
and__self
to 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>
);
}
}
children
is passed automatically for nested JSX.key
is 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
children
prop onP
when 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
AtomComponent
and implementrender()
. - Calling
setState
in the constructor → Not allowed; initialize withthis.state = { … }
in the constructor and usesetState
only after mount.
Go deeper with Element Creation (createElement
) to understand the lower-level building blocks behind the JSX runtime.