.iwc language reference
.iwc files are strict JSON with a fixed shape per file type. There is no separate syntax to learn: what you learn is the vocabulary — which keys each file takes, what values they accept, and three conventions on top of JSON: expressions, bindings and file references. This page is the map; each section links to the full guide.
File structure
Every file is one JSON object. Its type comes from the file name, <your-name>.<type>.iwc, except the two fixed files app.iwc and package.iwc.
{
"name": "About",
"route": "/about",
"layout": "main.layout.iwc",
"title": "About us",
"sections": [{ "type": "text", "variant": "h1", "content": "About us" }]
}Rules that apply to every file:
| Rule | Detail |
|---|---|
| Strict JSON | No comments, no trailing commas, no duplicate keys, no NaN/Infinity. A file two parsers could read differently is refused |
| Closed shape | Unknown keys are errors. Keys starting with x- are kept and ignored — data for other tools ("x-designer": {…}) |
| References by file name | "layout": "main.layout.iwc", "resource": "product.schema.iwc", "api": "login.api.iwc". Base names are unique per type, wherever the file lives |
| No secrets | Literal credentials are compile errors; write {"env": "NAME"} and set the variable |
| Positions | Every diagnostic names file:line:col |
The full list of keys per file type is in file types; app.iwc is in app configuration.
File types
| Type | Required keys | Guide |
|---|---|---|
app.iwc | name | App configuration |
package.iwc | name, iwc | App configuration |
.ui.iwc | name, route, sections | Screens & elements |
.layout.iwc | name, slots | Layouts |
.component.iwc | name, root | Components |
.schema.iwc | name, fields | Database |
.api.iwc | name, method, path — and exactly one of operation, action, workflow | API development |
.action.iwc | name, steps | Actions |
.workflow.iwc | name, resource, states, transitions | Workflows |
.event.iwc | name (plus payload and handlers) | Events |
.job.iwc | name, action | Jobs |
.state.iwc | name | Client state |
.security.iwc | name | Authentication |
.service.iwc | name, provider, methods | Services |
.integration.iwc | package, provider | Packages |
.widget.iwc | name, script | Widgets |
.test.iwc | name, cases | Debugging & testing |
Properties and data types
Values in .iwc files are JSON values. On top of them, IWC uses a few recurring formats, each validated by the compiler:
| Format | Example | Where |
|---|---|---|
| Identifier | cart, order_items | store, mutation and field names: letters, digits and _, not starting with a digit |
| Name | "Product page" | name of files: letters, digits, space, -, _ |
| Route | /products/:slug, /docs/* | screens (route), APIs (path, mounted under /api) |
| Duration | 500ms, 30s, 5m, 1h, 7d | caches, intervals, delays, session lifetimes |
| Ratio | 16/9 | image ratio |
| Rate | "10/1m" | security.rateLimit |
| Env reference | {"env": "SHOP_DATABASE_URL"} | any secret or environment-specific value |
| File reference | "product.schema.iwc" | any key that points at another file |
| Expression | "=price * quantity" | any string value that starts with = (below) |
Field types (schemas)
Tables declare their columns as fields with a type:
type | Stores | Rules |
|---|---|---|
string | short text | minLength, maxLength (default 10,000), pattern |
text | long text | maxLength |
integer, number | numbers | min, max |
boolean | true/false | |
email, url | validated text | |
password | a PBKDF2-SHA256 hash | always sensitive, never returned |
date, datetime | YYYY-MM-DD, ISO 8601 | |
enum | one of values | |
relation | the id of a row in to | onDelete: cascade, restrict, set null |
json | any JSON | |
file | a file reference |
Every field also takes required, unique, index, default, sensitive and description. See Database.
Field specs (requests and action input)
APIs (request.payload, request.query) and actions (input) validate incoming values with field specs:
{ "type": "string", "required": true, "minLength": 2, "maxLength": 120, "trim": true }| Key | Applies to |
|---|---|
type | string email url date datetime number integer boolean object array any |
required, default | all |
minLength, maxLength, pattern, trim | strings |
min, max | numbers |
enum | strings and numbers |
minItems, maxItems, items | arrays |
fields | objects (nested specs) |
Undeclared keys are always rejected. See Forms & validation.
Expressions
A string that starts with = is an expression, evaluated on the server:
{ "type": "text", "content": "=concat(product.name, ' — ', format(product.price, 'currency', 'USD'))" }| Values | numbers, 'text' or "text", true, false, null |
| Names | product.price, item.name, tags[0], list.length |
| Operators | + - * / %, == != < <= > >=, `&& |
| Functions | lists (sum avg min max count filter pluck first last sortBy join…), text (lower upper trim concat replace truncate…), values (coalesce if round number string…), dates (now date), display (format) |
In scope: the current record's fields, component props, the page's named data, and always params, query, user, auth, page and app. Expressions cannot loop, assign, define functions or reach the network, and each evaluation has a budget. Inside actions, condition, items and string set.value are expressions without the =. Full reference: Expressions & page data.
Conditions
Conditional rendering uses visible, decided on the server — hidden elements are never sent to the browser:
{
"type": "column",
"elements": [
{ "type": "link", "label": "Sign in", "target": "/login", "visible": { "auth": "signed-out" } },
{ "type": "link", "label": "Admin", "target": "/admin", "visible": { "roles": ["admin"] } },
{ "type": "badge", "content": "Low stock", "visible": "=product.stock < 5" }
]
}In business logic, conditions are steps: require fails the action unless a condition holds, and if branches:
{ "operation": "require", "condition": "product.stock >= input.quantity", "status": 409, "error": "=concat('Only ', product.stock, ' left')" }Bindings
Bindings connect elements to data without expressions:
| Key | Binds | Example |
|---|---|---|
data + item | a container to a list of records; item repeats per record | "data": {"source": "DATABASE.products"} |
bind | an element's text | "bind": "name", "user.name", "params.slug", "state.cart.count" |
binds | attributes | "binds": {"source": "image", "alt": "name"} |
fields | a card's slots | "fields": {"title": "name", "link": "/products/:slug"} |
value | a form input's initial value | "value": "item.title" |
| route patterns | links filled from the record | "target": "/products/:slug" |
Data sources are DATABASE.<table>, API.<name>, STATIC, USER and STATE.<store>.<list>. See Data binding.
Events and actions
There are three kinds of "something happens":
- UI actions on buttons and links:
navigate,open,close,toggle,scroll,state,api,logout,share,install,notifications,sync,pay,tracking,native. Forms havesubmit.success(message,reset,navigate,redirect,mutation,reload). See the button element. - Server actions:
.action.iwcfiles run by APIs, other actions (callAction), events and jobs. - Events and jobs: an action
emits an event; the.event.iwcfile'shandlersrun an action or queue a job. A.job.iwcruns an action in the background — queued withenqueue, or on aschedule.
Reusable definitions
| To reuse | Use |
|---|---|
| A piece of UI | a component with props and slots |
| The frame around pages | a layout with slots |
| Business logic | an action, called by several APIs or by other actions (callAction) |
| Styling | theme tokens in app.iwc and classes in styles/*.css |
| An external API | one .service.iwc or .integration.iwc, called from any action |
Validation
Validation happens at three points:
- Compile time —
iwc checkvalidates every file against its schema and every reference. A project that compiles has no dangling references. - Request time — every API validates its body, query and path parameters against its field specs and rejects undeclared fields with
422. - Write time — schema field rules are enforced on every write, including writes from actions, so no path stores an invalid row.
Error handling
| Where | How |
|---|---|
| Data blocks | empty (no records) and error (the source failed) elements |
| Forms | field errors under each input; the message above the button |
| API buttons | the error message in place; success decides what happens on 2xx |
| Actions | a failing step stops the action, rolls back its transaction, and answers with its status and error |
| Pages | the notFound screen in app.iwc for unknown routes and missing records |
{
"type": "stack",
"data": { "source": "API.products" },
"item": { "type": "text", "bind": "name" },
"empty": { "type": "text", "content": "No products yet." },
"error": { "type": "text", "content": "Products could not be loaded." }
}JSON Schemas
iwc schema --out <dir> writes one JSON Schema (draft 2020-12) per file type; iwc create puts them in .vscode/iwc-schemas/ and maps them in .vscode/settings.json, so VS Code completes and checks every key as you type. The same schemas drive the element reference.
Source: apps/marketing/content/docs/language.md