Skip to main content
The canonical form of an elementor-jsx site is an fs-project: a directory where the layout is the site wiring, and each file holds exactly one concern. exjsx build <dir> discovers it, validates the whole layout up front, and throws one error listing every problem with its fix.
Pages hold content, parts hold chrome, theme holds tokens, 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:
  • slug values must be unique and kebab-case (exjsx lint enforces both, rule ids duplicate-page-slug and page-seo).
  • template defaults to elementor_canvas for standalone pages. When the project has parts/ chrome, pages default to elementor_header_footer automatically; an explicit meta.template wins.

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:
Short generic names (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.
The prelude provides Nav, Footer, and Layout as built-in free variables. A project component named Nav gets silently shadowed by the built-in. Name yours SiteNav and SiteFooter, and import them explicitly from your components/ directory.

Component conventions

  • Small, props-driven function components; theme arrives via context (<Page theme={t}> plus useTheme()), 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 shows card, not c-1x9fq2.
  • Never reuse a kit-node instance (tabs(), divider() results). Shared instances mint duplicate ids and assertTree throws. 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.