Project structure

project-structure.md
Raw file

A scaffolded Lux app has a fixed file layout: two manifests, an app-level lifecycle module, a theme file, and directories for pages, widgets, and regions.

YAML
my-app/├── aiux.json              # Lux app manifest├── now.config.json        # ServiceNow scope manifest (now-sdk)├── package.json           # Depends on @servicenow/karuna + @servicenow/sdk├── application.js         # App-level lifecycle hooks (setup, navigate, teardown)├── theme.js               # AppTheme tokens (colors, radii)├── pages/                 # File-based routing (one directory per route)│   ├── layout.js          # App chrome (header, nav) wrapped around every page│   ├── home/│   │   ├── page.js        # AIUXElement page (default export)│   │   └── components/    # Components co-located with the page that uses them│   │       └── aiux-feature-card.js│   └── incidents/│       └── page.js        # Page with `static async loader(ctx)`├── widgets/               # Custom AIUXWidgetElement widgets (+ server scripts)│   └── aiux-hello-world/│       ├── index.js│       └── server-script.js├── regions/               # Region DSL files (one thunk per named region)├── eslint.config.mjs      # ESLint flat config (aiux plugins)├── scripts/               # setup-agent-skills.mjs (postinstall helper)├── AGENTS.md              # Canonical guidance for AI coding agents├── CLAUDE.md / GEMINI.md  # Point back to AGENTS.md├── .claude/               # Hooks + settings for Claude Code└── .nvmrc / .gitignore

What each entry is for

Entry Purpose
aiux.json Lux app manifest (the experience's declaration).
now.config.json ServiceNow scope manifest that now-sdk reads on build and deploy. Your app has two manifests, not one.
package.json Dependency and script declarations. @servicenow/karuna and lit are dependencies; @servicenow/sdk, the eslint-plugin-aiux-* rules, and the agent packs are dev dependencies. Holds the dev, build, serve, deploy, and lint scripts.
application.js App-level lifecycle hooks: setup(), navigate(), and teardown().
pages/layout.js The layout every page in the experience renders inside (header, navigation, footer). It extends NavLayout and is not re-created when the user navigates between routes.
theme.js The experience's theme tokens: colors, radii.
pages/ File-based routing. Each subdirectory is a route, and page.js in that directory is an AIUXElement page (the file's default export, optionally with static async loader(ctx) for server-side data fetching).
pages/<name>/components/ Components scoped to the page that uses them, co-located rather than shared globally.
widgets/ Custom AIUXWidgetElement widgets, each with an index.js and an optional server-script.js.
regions/ Region DSL files. Each one default-exports a nullary thunk returning a region template, and its region name comes from its path. region can't be imported from anywhere else.
eslint.config.mjs ESLint flat config with the eslint-plugin-aiux-* rules.
scripts/ Build and setup helpers, such as setup-agent-skills.mjs, run through postinstall.
AGENTS.md, CLAUDE.md, GEMINI.md AI coding agent guidance. CLAUDE.md and GEMINI.md point back to AGENTS.md.
.claude/ Hooks and settings for Claude Code.
.nvmrc, .gitignore Standard Node project files.

Note the split between the two files at the app root that are easy to confuse. pages/layout.js is the layout (the persistent chrome your pages render inside, and a widget class in its own right). application.js is the app's lifecycle module: three hooks, no markup. They are separate files with separate jobs.

Fields in aiux.json

Field What it defines
name The human-readable application name.
basename The URL prefix (everything under /aiux/<basename> is this experience).
landing The route served when someone opens the app with no path.
development.token A basic-auth token used only for local development.
build.exclude Glob patterns kept out of production builds.
experienceAcls Who is allowed into the experience at all.
scope / scopeId The ServiceNow application scope that owns the records. Added at deploy time, not by hand.

What the tree compiles to

Deploying compiles each source file into its corresponding platform record.

Source What it becomes
aiux.json The experience itself (name, URL prefix, landing page, per-experience settings, and any menus declared in it)
pages/<route>/page.js One page record per page
pages/layout.js The app's layout
widgets/<name>/index.js Reusable, discoverable widgets
theme.js Theme tokens
Static assets Attachments in your scope (images, fonts, JSON)
Compiled bundles JavaScript attachments on the widget records, served by the runtime

Pages, widgets, layouts, and internal helper components all compile to the same kind of widget record. A category field is what distinguishes them: page, layout, discoverable, or internal. So there's one schema to learn, not three.

Related topics

Core concepts

Build with Lux

Search documentation
Search document titles and file paths on the brazil branch