Nexanexa

Nexa Reference

Everything Nexa has, organized as a catalog — just like other frameworks' component reference. Search by name or filter by category; each card leads to a real, functional example.

Core and pages

File-based routing

Every .tsx under src/pages/ is a real route. [slug].tsx captures a dynamic segment; [locale] is the special segment that turns on i18n for that whole branch.

Core and pages

A page, top to bottom

load/paths/seo/schema are literal objects/templates — never executed; nexa-parser recognizes them as fixed patterns and resolves them itself against real data at build/request time. That's why they can't hold logic: no conditionals, no .map(), no functions of your own.

Core and pages

load() — fetching data

A real HTTP GET against NEXA_API_URL + url, with :params substituted by the route's real values. The backend can be any language — Nexa only expects JSON back.

Core and pages

paths() — pre-rendering dynamic routes

Same shape as load, but the backend responds with an array of parameter sets. nexa build generates a real index.html for each entry — plain HTML, with no dependency on the backend still running.

Core and pages

<For> — iterating lists

Not a component or an HTML element — a fixed syntactic pattern, just like t("key"). Rust classifies the callback's body once (it's a template) and renders it as real HTML once per array element.

Core and pages

Automatic active link

An <a href> whose href matches the current route gets aria-current="page" on its own — no conditional to write (a Nexa page has no logic of its own to write one in). data-nexa-match="prefix" turns it into prefix matching, with a segment boundary.

Core and pages

Shared layouts

src/layout.tsx, optional, wraps every project page's HTML. It compiles just like any page — this isn't composition: nexa-cli finds the single empty data-nexa-slot and inserts the page's already-rendered HTML there, verbatim.

Core and pages

export const head

The only thing that actually reaches the document's real <head> (everything else the layout returns ends up inside <body>, via the slot splice). icon/appleTouchIcon/stylesheets/meta — a literal object, same pattern as seo/schema.

Core and pages

Header with real state: an island in the layout

A direct onClick in src/layout.tsx isn't supported (node ids would collide between the layout and the page). An island is — the client reads specifier/props/strategy from the DOM, with no activation-manifest entry, and survives SPA navigation without remounting.

SEO and i18n

seo{}

Per-page title/description/canonical/Open Graph/Twitter card. Each field is a string, a template, or t("...") — never a function. A field that can't resolve gets omitted, never filled in halfway.

SEO and i18n

schema{} — real JSON-LD

Resolves to a real <script type="application/ld+json">. type/@type is interchangeable; an explicit @type never gets overwritten.

SEO and i18n

Automatic sitemap.xml and robots.txt

nexa build generates both for every compiled static route. nexa-seo warns (NEXA-SEO-001..) if title/description/canonical is missing, or an image lacks alt — it never blocks the build.

SEO and i18n

Basic i18n: t() and [locale]

src/locales/<locale>.json is a flat dictionary. t("home.title") resolves against the current locale's dictionary — no fallback to another language: a missing key gets omitted (in seo) or stays inert in the body.

SEO and i18n

Interpolation in t()

t("key", { variable: data.x }) substitutes the matching placeholder inside the dictionary text. nexa build/nexa lint fail with a clear error if a key and its arguments don't match up.

SEO and i18n

Automatic hreflang

<link rel="alternate" hreflang="..."> is generated automatically as long as the route lives under [locale] and there's more than one src/locales/*.json — nothing to write by hand.

Interactive islands

Marking an island

data-nexa-island is a string literal, resolved against nexa.toml [imports] — Rust never opens or interprets the module it points to. The fallback (the div's children) resolves with the same mechanism as any other content: data.*, t(), <For> included.

Interactive islands

The mount() contract

The module must default-export a mount(el, props) function that optionally returns a cleanup function. You can write it by hand with @nexa/reactivity, or mount a real framework.

Interactive islands

Real Vue inside an island

A real Vue (or other framework) component, with Rust never reading or understanding it — it runs 100% on the client. A Vue Router mounted inside coexists without conflict with Nexa's SPA navigation.

Interactive islands

An npm library inside an island

Different from [imports]: an island file gets bundled in full with esbuild (--bundle --format=esm), so a normal import at the top resolves just like in any TypeScript project.

Interactive islands

Lightweight component model (islands only)

Doesn't apply to Nexa Core pages. Component<P>, mountComponent(), and a ComponentContext that chains children's cleanup to the parent's — no Virtual DOM, exclusively for code inside a native island.

@nexa/ui — Visual components

Button

nx-btn + a variant (primary/outline). Nothing to declare — nexa build detects the nx-* classes and generates the matching CSS.

@nexa/ui — Visual components

Input

nx-input. With aria-invalid="true" (which @nexa/forms adds on its own) it already has a red border, nothing extra to declare.

@nexa/ui — Visual components

Card

nx-card + an optional nx-card-title.

@nexa/ui — Visual components

Table

nx-table-wrap adds horizontal scroll on small screens; nx-table-zebra alternates row backgrounds.

@nexa/ui — Visual components

Badge

nx-badge + a variant: primary/success/warning/danger/neutral.

@nexa/ui — Visual components

Avatar

nx-avatar (a circle, 2.5rem by default) + nx-avatar-sm/-lg. With an <img> inside it crops via object-fit: cover.

@nexa/ui — Visual components

Breadcrumbs

nx-breadcrumbs (container) + nx-breadcrumbs-link — the "/" between steps is plain CSS (::after), nothing to write by hand. aria-current="page" marks the current step.

@nexa/ui — Visual components

Alert

nx-alert + a success/warning/danger variant (colored left border). An optional nx-alert-title.

@nexa/ui — Visual components

Divider

nx-divider (horizontal) / nx-divider-vertical.

@nexa/ui — Visual components

Tooltip

nx-tooltip-content shows on :hover/:focus-within — keyboard accessible, zero lines of JS.

@nexa/ui — Visual components

Progress

nx-progress-bar needs its width set by hand (fixed, or dynamic via data.*) — Nexa doesn't compute any percentage.

@nexa/ui — Visual components

Skeleton

nx-skeleton is a block with a shimmer animation; the developer sets whatever width/height it needs.

@nexa/ui — Visual components

Layout utilities

A small, deliberate set, not a full Tailwind-like utility system: nx-flex/nx-gap-N/nx-grid/nx-grid-cols-N/nx-stack.

@nexa/ui — Visual components

Dark theme

With zero JS, the site already respects the system's prefers-color-scheme. platform.theme.set("dark") lets the user choose explicitly and override the system, persisted with platform.storage().

@nexa/ui — Interactive, no island

Dialog

ui.openDialog(el)/ui.closeDialog(el) — trapped focus, Escape closes it, returns focus to the previous element. ui is always available, nothing to declare in [imports].

@nexa/ui — Interactive, no island

Dialog stacking

Opening a Dialog while another is already open stacks it on top (correct z-index) and hides the one below from screen readers (aria-hidden) while it's covered.

@nexa/ui — Interactive, no island

ui.confirm() / ui.alert()

Confirm before a destructive action, or notify that something was saved, without writing the <div class="nx-dialog"> by hand. Escape in confirm() counts as canceling; in alert(), as accepting.

@nexa/ui — Interactive, no island

Drawer

Same accessibility contract as Dialog, meant for sidebars/side menus instead of a centered box.

@nexa/ui — Interactive, no island

Toast / snackbar

ui.notify() — not to be confused with platform.notify() (a real OS notification): this one is ephemeral, inside the page. It stacks with others, auto-closes after 4s.

@nexa/ui — Interactive, no island

Tabs

ui.selectTab(event) reads which button was clicked and its data-tab, finds the nearest .nx-tab-group, and does the rest.

@nexa/ui — Interactive, no island

Accordion

ui.toggleAccordionItem(event). With data-exclusive="true" on the container, opening one item closes the rest.

@nexa/ui — Interactive, no island

Dropdown

ui.toggleDropdown(event) / ui.selectDropdownOption(event) — closes on an outside click or Escape, without trapping focus (a dropdown doesn't block the rest of the page).

@nexa/ui — Interactive, no island

Sorting and filtering a Table

ui.sortTable(event) sorts by the clicked <th>'s column (numeric if both cells are). ui.filterTable(table, query) hides rows whose text doesn't match. Both client-side, no re-fetch.

@nexa/ui — Interactive, no island

Reactive page search

ui.createPageSearch(container) filters data-searchable elements with a real @nexa/reactivity Signal underneath. The controller hangs off the <input> itself because every chunk is an isolated module.

@nexa/forms

Validation with data-nexa-form

data-nexa-form turns everything else on. Each field needs a name, connected to <p data-nexa-error-for="that-name">. It validates on first blur, and on every change after that.

@nexa/forms

Custom messages and automatic classes

data-nexa-message replaces the browser's native message. Automatic classes per field: nx-touched, nx-dirty, nx-invalid, plus aria-invalid.

@nexa/platform

platform.share()

Web: Web Share API. Capacitor: the @capacitor/share plugin. Tauri: not supported (throws).

@nexa/platform

platform.notify()

A real OS-level notification. Web: Notification API (asks for permission). Capacitor: @capacitor/local-notifications.

@nexa/platform

platform.capturePhoto()

Web: getUserMedia + canvas. Capacitor: @capacitor/camera. Returns { dataUrl }.

@nexa/platform

platform.storage() / sessionStorage()

The same get/set/remove contract over localStorage or sessionStorage — the latter lives only as long as the tab does.

@nexa/platform

platform.cache()

Direct Cache API, independent from [pwa.cache] (which lives in the service worker) — for caching something specific from regular code.

@nexa/platform

platform.db()

A minimal wrapper over IndexedDB, not an ORM. Each collection name is its own database — nothing to declare upfront.

@nexa/platform

platform.network

Native Capacitor uses the real @capacitor/network; Web/Tauri use navigator.onLine + the online/offline events. type (wifi/cellular) is Capacitor-only.

@nexa/platform

platform.lifecycle

Native Capacitor uses @capacitor/app (getState/appStateChange); Web/Tauri use document.hidden + visibilitychange — the same signal @capacitor/app's own web branch uses.

@nexa/platform

platform.geolocation

Native Capacitor uses @capacitor/geolocation; Web/Tauri use navigator.geolocation — the same API @capacitor/geolocation's own web branch uses internally.

@nexa/platform

platform.deepLinks

Native Capacitor uses @capacitor/app (getLaunchUrl/appUrlOpen). Web/Tauri: getLaunchUrl() always returns an empty url and onOpen() never fires — that's exactly how @capacitor/app's own web branch behaves.

@nexa/platform

platform.push

Deliberately smaller than the full Capacitor plugin: registration + receiving only. register() returns a discriminated union because Capacitor and Web don't share a registration shape.

@nexa/platform

platform.biometrics

Native Capacitor uses @aparajita/capacitor-biometric-auth. Web/Tauri use real WebAuthn — that plugin's own web branch wasn't replicated because it's a deliberate simulation, not real biometrics.

@nexa/platform

platform.haptics

Native Capacitor uses the real @capacitor/haptics. Web/Tauri use navigator.vibrate — the same API @capacitor/haptics' own web branch uses.

@nexa/reactivity

state() — Signal

.value reads and subscribes; .peek() reads without subscribing. Writing .value fires the effects that read it, batched by microtask.

@nexa/reactivity

computed()

A ReadonlySignal that recalculates on its own. Chaining computeds works, at a real cost: each hop is one more microtask round-trip.

@nexa/reactivity

effect()

Runs right away, and again every time something it read changes. Returns a function to turn it off.

@nexa/reactivity

watch()

Like effect, but does NOT run when created — only on later changes. Receives the new and the old value.

@nexa/reactivity

bindText()

What the compiler automatically generates for {data.x} in a page's body — not something you'd normally write by hand.

@nexa/reactivity

mountComponent() — islands only

Doesn't apply to Nexa Core pages. Component<P>, typed props, and chained cleanup — no Virtual DOM.

@nexa/http

createApi()

Real get/post/put/delete. A non-2xx status throws HttpError { status, message }.

@nexa/http

query()

data/loading/error as @nexa/reactivity Signals. cachePolicy: cache-first (default) | network-first | stale-while-revalidate.

@nexa/http

mutation()

The same Signals (data/loading/error) as query(), for an action the user triggers (create, edit, delete).

@nexa/http

connectSSE()

A thin wrapper over the native EventSource. data is the last message, parsed as JSON when possible.

@nexa/http

connectSocket()

A wrapper over the native WebSocket. status reflects the real lifecycle; send() serializes objects as JSON automatically.

@nexa/http

Reconnection with backoff

reconnect: true (or an object to tweak the defaults) reconnects on its own with exponential backoff + jitter. status passes through "reconnecting" while it tries.

Third parties and vendoring

Third-party CSS: a plain <link>

Nexa never processes or understands that CSS, so no special step is needed.

Third parties and vendoring

JS with a global build (UMD): a plain <script>

Leaves a global variable — since a chunk copies the handler verbatim, you can use it directly in an onClick, nothing to declare in nexa.toml.

Third parties and vendoring

Pure ES modules: [imports]

The same mechanism platform/ui use. The chunk detects name. in the handler and prepends the import.

Third parties and vendoring

Vendoring a real npm package

When it has its own dependencies or ships as CommonJS: install it, write an entry.js, and esbuild --bundle --format=esm into a single file under public/vendor/.

Images, PWA and performance

Automatically optimized images

No config needed: any static <img src> wider than 480px turns into a <picture> with real AVIF variants at several widths, with width/height/loading="lazy" filled in on their own.

Images, PWA and performance

PWA

nexa add pwa adds [pwa] to nexa.toml. nexa build generates a real manifest.webmanifest and sw.js; every page carries the <link rel="manifest"> and the service worker registration on its own.

Images, PWA and performance

Performance budgets

With this declared, nexa build fails (not just warns) if a page goes over — naming exactly what was exceeded.

Images, PWA and performance

Telemetry

Without the explicit endpoint, @nexa/telemetry never loads. With it, every page sends Core Web Vitals (LCP/CLS/INP) and uncaught errors.

Native, deployment and CLI

Tauri (desktop)

Generates a real Windows/macOS/Linux binary, compiled with cargo — the same source code as the web version.

Native, deployment and CLI

Capacitor (Android/iOS)

Generates a real Android/iOS project. The Android build was verified end to end (a real APK compiled with Gradle); iOS needs Xcode.

Native, deployment and CLI

Deploying with nginx

Generates a real deploy/nginx.conf: gzip, security headers, a year-long immutable cache only for hashed files, and a real proxy_pass for dynamic routes without paths.

Native, deployment and CLI

The full CLI

nexa build/preview/dev/lint/test/add/info — lint fails if there are SEO/package warnings; test compiles (a fresh dist/) and runs the project's test runner.

Native, deployment and CLI

nexa.toml — full reference

Every section is optional except [project]. None of them require package.json or npm.

Native, deployment and CLI

@nexa/test

For testing an isolated activation chunk, outside a real browser — mountChunk/getEntry/expectEntry against the real manifest.