exjsx build <dir> discovers it, validates the whole layout up front, and
throws one error listing every problem with its fix.
data() holds data.
A file that mixes concerns still compiles; the layout exists so the seams stay
reviewable. Small sites may instead use a single site.jsx default-exporting
defineSite; it runs through the same pipeline.
Page meta
Every*.page.jsx default-exports a component and may export meta:
slugvalues must be unique and kebab-case (exjsx lintenforces both, rule idsduplicate-page-slugandpage-seo).templatedefaults toelementor_canvasfor standalone pages. When the project hasparts/chrome, pages default toelementor_header_footerautomatically; an explicitmeta.templatewins.
Theme tokens
theme.mjs is the one place brand values live. Components receive it as the
{ theme } prop:
mode: 'literal' emits colors and fonts directly and renders on every supported
Elementor version. mode: 'var' makes text color and font live-editable in
Elementor’s Class Manager (variables); backgrounds degrade to literals
automatically on 4.1.4, and the compiler handles that for you. Start literal;
opt into var when you need live binding.
The prelude: import nothing
The build auto-injects a curated authoring surface into every built file, so components and pages usually need zero imports.defineSite, defineTheme,
fontLoader, tabs, dyn, the kit builders, and the component library all
resolve as free variables. Unused names tree-shake away, and your own binding of
a name always wins, so the prelude can never break explicit code.
Everything else comes from one bare specifier:
S, C, DIM, node, abs, hover) are deliberately
not auto-injected: a typo silently resolving to a helper is worse than a
ReferenceError. They stay explicit-import-only.
Component conventions
- Small, props-driven function components; theme arrives via context
(
<Page theme={t}>plususeTheme()), not prop-drilling. - Any visual pattern used three or more times gets
cls="card": the dedup pass names the shared class, and the Class Manager showscard, notc-1x9fq2. - Never reuse a kit-node instance (
tabs(),divider()results). Shared instances mint duplicate ids andassertTreethrows. Use a factory:const Rule = () => divider({...}). - Three or more pages with the same shape: use
fromData(items, map), not copy-paste.
Next
Styling
tw, sx, and raw: the styling ladder.
Components
Intrinsics and kit helpers.