Xuda logo

xuda.io

Technical docs for Xuda Slim and Xuda CLI

Product overview
Xuda CLI

xuda-cli

xuda-cli is the local development CLI for Xuda app projects.

Xuda CLI pages

Repo-driven docs from the xuda-cli package README and generated folder guides.

Docs page

xuda-cli is the local development CLI for Xuda app projects.

It lets you:

  • create and run a Xuda app locally
  • author the app in friendly source files such as App.xu, components/*.xu, and tables/*.json5
  • compile those source files into runtime JSON docs
  • keep the runtime docs in one canonical location: node_modules/.project_data/
  • optionally sync cloud project docs down into source files and push local changes back up

Quick Start

Create a new app:

npm create xuda-cli@latest my-app
cd my-app
npm install
npm run dev

Typical package.json scripts:

{
  "scripts": {
    "dev": "xuda-cli dev",
    "sync": "xuda-cli sync"
  }
}

Install common plugins with normal npm install:

npm install @xuda.io/xuda-framework-plugin-tailwind
npm install @xuda.io/xuda-dbs-plugin-xuda

There is no plugin subcommand in xuda-cli.

Commands

xuda-cli dev
xuda-cli sync
xuda-cli --help

xuda-cli dev

  • loads your project from the current directory
  • compiles source files into node_modules/.project_data/
  • serves the app locally
  • watches .xu and supported .json5 files and regenerates runtime docs on save

xuda-cli sync

  • requires api_key in xuda.config.js
  • downloads cloud docs into node_modules/.project_data/
  • creates missing local .xu and .json5 source files from those docs
  • recompiles local source back into node_modules/.project_data/
  • uploads changed docs back to the cloud

Mental Model

The app has three important layers:

  1. index.html This is the document shell. It owns the <head>, metadata, fonts, and the HTML node where your root app mounts.
  2. App.xu This is the root Xuda screen. It is compiled as the program id app.
  3. node_modules/.project_data/ This is the generated runtime document set that the Xuda runtime reads.

If xuda.config.js uses:

module.exports = {
  entry: 'index.html'
};

then index.html is served first, and App.xu is automatically mounted into #app unless you already mounted another component into that HTML entry.

If entry is omitted, xuda-cli defaults to app.

Recommended rule:

  • keep index.html as the page shell
  • keep App.xu as the root Xuda screen
  • add feature screens under components/

How xuda-cli Technology Works

xuda-cli is not just a file server. It is a source compiler, runtime packager, local app host, and optional cloud sync tool.

When you work on a Xuda app, the technology flow looks like this:

  1. Authoring layer You write human-friendly source files such as App.xu, components/*.xu, data/get/*.xu, and tables/*.table.json5.
  2. Compile layer xuda-cli parses those source files and converts them into the JSON document model that the Xuda runtime already understands.
  3. Runtime docs layer The compiled output is written to node_modules/.project_data/. This is the canonical runtime doc folder used by the local app.
  4. Runtime host layer xuda-cli dev serves your index.html, runtime assets, compiled component scripts, and the generated document set.
  5. Browser/runtime execution layer The browser loads the Xuda runtime, mounts App.xu or the configured entry program, and executes fields, actions, events, datasources, and panels.
  6. Optional cloud sync layer If api_key is configured, xuda-cli sync can pull remote docs into the local project, create missing source files, and push changed docs back to the cloud.

Source-first philosophy

The intended developer experience is:

  • source files are the place humans edit
  • node_modules/.project_data/ is generated runtime output
  • the runtime reads generated docs
  • sync can import older or remote docs into source files, but source remains the preferred authoring format

What gets compiled

xuda-cli compiles:

  • .xu files into component or logic-unit docs
  • .table.json5 files into table docs
  • .route.json5 files into route docs
  • .agent.json5 files into AI agent docs

What stays runtime-facing

The local runtime still consumes the classic Xuda document shape:

  • progUi
  • progFields
  • progEvents
  • progDataSource
  • tableFields
  • tableIndexes
  • routeMenu
  • agentConfig

That means xuda-cli gives you a friendlier source format without requiring a brand-new runtime protocol.

SSR and render profiles

xuda-cli supports:

  • ssr: 'none'
  • ssr: 'first_page'
  • ssr: 'full'

In practice:

  • none normal client rendering
  • first_page first-page SSR with client takeover
  • full fuller SSR with hydration-friendly behavior

Plugin model

Plugins are standard npm dependencies, not copied folders and not special CLI subcommands.

Use plugins for:

  • UI frameworks such as @xuda.io/xuda-framework-plugin-tailwind
  • database behavior such as @xuda.io/xuda-dbs-plugin-xuda

Why the project keeps both index.html and App.xu

They solve different problems:

  • index.html document shell, metadata, fonts, mount element
  • App.xu root Xuda UI, fields, actions, panels, and app behavior

This separation keeps the app structure predictable and makes it easier to scale into multiple routed or panel-based screens.

Project Layout

This is the official source layout:

my-app/
  package.json
  xuda.config.js
  index.html
  App.xu

  components/
    **/*.xu

  globals/
    **/*.xu

  data/
    get/**/*.xu
    set/**/*.xu
    batch/**/*.xu

  apis/
    **/*.xu

  scripts/
    **/*.xu

  alerts/
    **/*.xu

  tables/
    **/*.table.json5

  routes/
    **/*.route.json5

  ai-agents/
    **/*.agent.json5

  node_modules/
    .project_data/

Rules:

  • App.xu is the reserved root app file
  • components/ is for UI components
  • globals/ is for shared state units
  • data/get/, data/set/, and data/batch/ are for logic units
  • tables/, routes/, and ai-agents/ are structured JSON5 resources
  • node_modules/.project_data/ is generated runtime output, not your source of truth

What Lives in index.html

Use index.html for:

  • the page title
  • meta tags
  • favicons
  • fonts
  • external scripts if you need them
  • the root mount element

Minimal example:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My Xuda App</title>
  </head>
  <body>
    <main id="app"></main>
  </body>
</html>

xuda.config.js

xuda.config.js controls entry, server, and sync-related settings.

Example:

module.exports = {
  entry: 'index.html',
  port: 3000,
  ssr: 'none',
  api_key: '',
  build_id: ''
};

Supported fields:

  • entry The app entry. Usually index.html. Defaults to app.
  • port Local dev port. Defaults to 3000.
  • ssr One of none, first_page, or full.
  • api_key Optional. Required for npm run sync.
  • build_id Optional remote build target. If provided, it requires api_key.

package.json

package.json is the source of app package metadata:

  • name
  • version
  • description

Minimal example:

{
  "name": "my-app",
  "version": "0.1.0",
  "description": "Starter Xuda app",
  "scripts": {
    "dev": "xuda-cli dev",
    "sync": "xuda-cli sync"
  },
  "dependencies": {
    "@xuda.io/xuda-framework-plugin-tailwind": "latest",
    "@xuda.io/xuda-dbs-plugin-xuda": "latest"
  },
  "devDependencies": {
    "xuda-cli": "latest"
  }
}

Source File Types

xuda-cli supports these authored source types:

| Location | File type | Xuda unit | | --- | --- | --- | | App.xu | .xu | root component | | components/**/*.xu | .xu | component | | globals/**/*.xu | .xu | globals | | data/get/**/*.xu | .xu | get_data | | data/set/**/*.xu | .xu | set_data | | data/batch/**/*.xu | .xu | batch | | apis/**/*.xu | .xu | api | | scripts/**/*.xu | .xu | javascript | | alerts/**/*.xu | .xu | alert | | tables/**/*.table.json5 | .json5 | table | | routes/**/*.route.json5 | .json5 | route | | ai-agents/**/*.agent.json5 | .json5 | ai_agent |

Xuda Building Blocks At A Glance

Each block exists for a different responsibility. A good Xuda app is usually clearer when each concern has its own unit instead of pushing everything into one giant component.

| Building block | Main responsibility | Best used for | | --- | --- | --- | | component | UI and interaction | screens, panels, forms, dashboards, reusable views | | globals | shared app state | signed-in user, campus, feature flags, shared filters | | get_data | reading data | list loads, record queries, reusable data fetches | | set_data | writing data | create, update, delete, persistence workflows | | batch | record-by-record processing | rebuild jobs, migration flows, recalculation | | api | response-oriented logic | API outputs, summaries, machine-readable endpoints | | javascript | custom JS logic | formatting, transformations, reusable computation | | alert | user messaging | toasts, success notices, warnings, error feedback | | table | data model definition | fields, indexes, database-facing schema metadata | | route | navigation model | menus, destinations, route planning | | ai_agent | assistant configuration | app-specific AI helpers and future agent workflows |

How to choose the right block

Use component when:

  • the user sees it
  • it owns UI state or user interactions
  • it renders template markup

Use globals when:

  • more than one screen needs the same state
  • the value is app-wide rather than screen-local

Use get_data when:

  • the main job is to read from a datasource
  • you want loading logic to stay outside UI components

Use set_data when:

  • the main job is to persist a change
  • you want writes to have a named, reusable unit

Use batch when:

  • you are processing many rows or records
  • the workflow is iterative and not just a simple read/write

Use api when:

  • the result should be shaped as a clean response
  • the unit feels more like an endpoint than a screen

Use javascript when:

  • you need custom code but do not want it scattered through actions
  • the logic is reusable and named

Use alert when:

  • the app needs consistent messaging
  • a message should be reusable across multiple screens or workflows

Use table when:

  • a datasource refers to real table-backed storage
  • you need fields and indexes with a stable id

Use route when:

  • the app has named destinations or menu structure
  • you want a navigation model that is visible in source

Use ai_agent when:

  • the app needs a configured assistant resource
  • you want that assistant defined as a first-class app asset

.xu Blocks

xuda-cli reads .xu files as block-based source files.

Supported component blocks:

  • <meta lang="json5">
  • <params lang="json5">
  • <fields lang="json5">
  • <actions lang="json5">
  • <events lang="json5">
  • <datasource lang="json5">
  • <template>
  • <script>
  • <style>
  • <mount>

Supported non-UI logic blocks:

  • globals <meta>, <fields>, <events>, optional <datasource>
  • get_data, set_data, batch <meta>, <params>, <fields>, <events>, <datasource>
  • api <meta>, <params>, <fields>, <events>, <datasource>, <response>
  • javascript <meta>, <params>, <code>
  • alert <meta>, <params>, <alert>

Block rule:

  • the object blocks are JSON5-style object literals
  • unquoted keys are fine
  • arrays and nested objects are fine
  • App.xu does not need a <mount> block
  • ordinary components only need <mount> when you want them mounted directly into an HTML file

Template Language

Inside <template>, xuda-cli gives you a friendly source format and compiles it into Xuda runtime markup.

Friendly tag aliases

  • <single-view> becomes xu-single-view
  • <multi-view> becomes xu-multi-view
  • <panel> becomes xu-panel

Friendly attributes

  • bind="field_v" becomes xu-bind
  • if="@some_flag_v" becomes xu-exp:xu-render
  • for="@items_v" becomes xu-exp:xu-for
  • for-key="name" becomes xu-for-key
  • for-val="name" becomes xu-for-val
  • :content="@title_v" becomes xu-exp:xu-content
  • :rows_in="@rows_v" becomes xu-exp:rows_in
  • @click="saveItem" becomes xu-on:click
  • @click.stop="saveItem" becomes xu-on:click with stopPropagation
  • @submit.prevent="saveItem" becomes xu-on:submit with preventDefault

Recommended rule:

  • write explicit <single-view> or <multi-view> wrappers even though the compiler can add a root wrapper for you

Example:

<template>
  <single-view>
    <section class="screen">
      <input bind="search_v" placeholder="Search students">
      <button @click="togglePanel">Toggle panel</button>

      <div if="@show_panel_v">
        <panel program="student_details" />
      </div>
    </section>
  </single-view>
</template>

Component Example

component is the main UI building block in Xuda.

Use it for:

  • full screens
  • dashboards
  • forms
  • reusable panels
  • feature views such as students, tasks, profiles, or settings

A component usually owns:

  • visible layout
  • local screen state
  • UI-triggered workflows
  • panel composition
  • optional client-side mount logic and styles

Example components/StudentList.xu:

<meta lang="json5">
{
  id: "student_list",
  menuTitle: "Student List",
  uiFramework: "@xuda.io/xuda-framework-plugin-tailwind"
}
</meta>

<fields lang="json5">
{
  show_panel_v: { type: "boolean", value: false },
  student_count_v: { type: "number", value: 3 }
}
</fields>

<actions lang="json5">
{
  togglePanel: [
    { action: "update", name: { value: "@show_panel_v: !@show_panel_v" } }
  ]
}
</actions>

<template>
  <single-view>
    <section class="p-6 space-y-4">
      <h1>Students</h1>
      <p :content="'Count: ' + @student_count_v"></p>
      <button class="rounded-full px-4 py-2 bg-sky-600 text-white" @click="togglePanel">
        Toggle details
      </button>

      <div if="@show_panel_v">
        <panel program="student_details" />
      </div>
    </section>
  </single-view>
</template>

Optional direct HTML mount

If you want a component mounted directly into an HTML file, add a <mount> block:

<mount>
{
  id: "marketing_hero",
  file: "landing.html",
  selector: "#hero"
}
</mount>

Rules:

  • App.xu is always the root app and does not need <mount>
  • components used only through <panel program="..."> do not need <mount>
  • for non-root components, the program id resolves in this order:
  • meta.id
  • mount.id
  • file basename

Globals Example

globals is the shared-state building block.

Use it for:

  • signed-in operator identity
  • campus or tenant context
  • theme preferences
  • shared filters or flags used across multiple screens

Do not use globals for:

  • page-specific transient UI state that only one component needs
  • presentation concerns that belong inside a component

Example globals/SchoolGlobals.xu:

<meta lang="json5">
{
  id: "school_globals",
  menuTitle: "School Globals"
}
</meta>

<fields lang="json5">
{
  campus_name_v: { type: "string", value: "Boston Campus" },
  dark_mode_v: { type: "boolean", value: false }
}
</fields>

Use globals for shared state and app-wide values.

Datasource Format

Use the readable wrapper format inside <datasource lang="json5">.

none

<datasource lang="json5">
{
  type: "none"
}
</datasource>

Table datasource

<datasource lang="json5">
{
  type: "table",
  table: "student_profiles_table",
  filter: {
    mode: "index",
    activeIndex: "campus_idx",
    indexes: {
      campus_idx: {
        campus: {
          from: "@campus_in",
          to: "@campus_in"
        }
      }
    }
  },
  output: "students_out",
  direction: "asc",
  realtime: true,
  limit: 25
}
</datasource>

Query datasource

<datasource lang="json5">
{
  type: "table",
  table: "student_tasks_table",
  filter: {
    mode: "query",
    query: {
      $and: [
        { status: "Open" },
        { campus: "Boston Campus" }
      ]
    }
  },
  output: "tasks_out"
}
</datasource>

Array / JSON / CSV datasource

<datasource lang="json5">
{
  type: "json",
  source: {
    url: "https://example.com/students.json"
  },
  output: "students_out"
}
</datasource>

Rules:

  • type can be none, table, array, json, or csv
  • for table sources, use table, filter, sort, direction, realtime, limit, skip, and output
  • for array/json/csv, use source.field or source.url
  • if you need raw unsupported datasource keys, use:
<datasource lang="json5" mode="raw">
{
  "dataSourceType": "table",
  "dataSourceTableId": "student_profiles_table"
}
</datasource>

get_data Example

get_data is the read-focused building block.

Use it when:

  • the unit's main purpose is loading data
  • you want one reusable place for filters, limits, sorting, and output mapping
  • a component should call a named loader instead of owning query details directly

Typical outputs:

  • rows into an output field
  • filtered subsets of a table
  • external JSON / CSV / array-based data

Example data/get/LoadStudentClasses.xu:

<meta lang="json5">
{
  id: "load_student_classes",
  menuTitle: "Load Student Roster"
}
</meta>

<params lang="json5">
{
  in: {
    campus_in: "string"
  },
  out: {
    students_out: "array"
  }
}
</params>

<datasource lang="json5">
{
  type: "table",
  table: "student_profiles_table",
  filter: {
    mode: "index",
    activeIndex: "campus_idx",
    indexes: {
      campus_idx: {
        campus: {
          from: "@campus_in",
          to: "@campus_in"
        }
      }
    }
  },
  output: "students_out",
  direction: "asc",
  realtime: true,
  limit: 25
}
</datasource>

set_data Example

set_data is the write-focused building block.

Use it when:

  • the unit's main purpose is to create, update, or delete records
  • you want persistence to have a visible, testable home in source
  • several screens might share the same write behavior

Typical uses:

  • saving a profile
  • deleting a row
  • creating a new record
  • updating status or workflow state

Example data/set/SaveStudentProfile.xu:

<meta lang="json5">
{
  id: "save_student_profile",
  menuTitle: "Save Student Profile",
  crudMode: "U",
  allowCreate: true
}
</meta>

<params lang="json5">
{
  in: {
    student_id_in: "string",
    full_name_in: "string",
    email_in: "string",
    campus_in: "string"
  }
}
</params>

<datasource lang="json5">
{
  type: "table",
  table: "student_profiles_table"
}
</datasource>

Use set_data units to represent write behavior clearly in source instead of hiding persistence inside a component.

batch Example

batch is the iterative processing building block.

Use it when:

  • one action needs to process many rows
  • a workflow should run before_record or after_record
  • you are rebuilding, migrating, enriching, or recalculating data

Example data/batch/RebuildClassRoster.xu:

<meta lang="json5">
{
  id: "rebuild_class_roster",
  menuTitle: "Rebuild Class Roster"
}
</meta>

<events lang="json5">
{
  before_record: [
    { action: "delay", name: { value: "1" } }
  ]
}
</events>

<datasource lang="json5">
{
  type: "table",
  table: "student_profiles_table",
  output: "students_out"
}
</datasource>

api Example

api is the response-oriented building block.

Use it when:

  • the result should be returned as a structured response
  • the unit behaves more like an endpoint or service response than a screen
  • you want a clean place to translate fields and datasource results into a JSON-style payload

An api unit often combines:

  • params
  • datasource
  • optional events
  • response serialization in <response>

Example apis/StudentClassesApi.xu:

<meta lang="json5">
{
  id: "student_classes_api",
  menuTitle: "Student Classes API"
}
</meta>

<params lang="json5">
{
  out: {
    students_out: "array"
  }
}
</params>

<datasource lang="json5">
{
  type: "table",
  table: "student_profiles_table",
  output: "students_out"
}
</datasource>

<response>
({ ok: true, count: (@students_out || []).length, rows: @students_out })
</response>

javascript Example

javascript is the custom-code building block.

Use it when:

  • an expression becomes too large or too reusable to leave inline
  • a transformation deserves a name
  • you want custom logic without hiding it in component actions

Good uses:

  • string normalization
  • custom scoring
  • reusable formatting
  • derived value computation

Example scripts/NormalizeStudentName.xu:

<meta lang="json5">
{
  id: "normalize_student_name",
  menuTitle: "Normalize Student Name"
}
</meta>

<params lang="json5">
{
  in: {
    raw_name_in: "string"
  },
  out: {
    normalized_name_out: "string"
  }
}
</params>

<code>
return (@raw_name_in || "")
  .trim()
  .toLowerCase()
  .replace(/\b\w/g, (letter) => letter.toUpperCase());
</code>

alert Example

alert is the reusable messaging building block.

Use it when:

  • a toast or feedback message should be consistent across the app
  • several workflows need the same success or error message
  • you want user messaging to live in its own source file instead of being embedded in UI logic

Example alerts/StudentSaved.xu:

<meta lang="json5">
{
  id: "student_saved",
  menuTitle: "Student Saved"
}
</meta>

<alert lang="json5">
{
  alertDisplay: "toast",
  alertType: "success",
  alertTitle: "Student saved",
  createLog: false
}
</alert>

Table Example

table is the data model building block.

Use it when:

  • your app stores structured records
  • get_data or set_data units target database-backed tables
  • you need indexes that match real query patterns

Tables usually define:

  • field ids
  • field types
  • indexes
  • stable resource ids used by datasource units

Example tables/student-profiles.table.json5:

{
  id: "student_profiles_table",
  menuTitle: "Student Profiles Table",
  fields: [
    { field_id: "student_id", type: "string" },
    { field_id: "full_name", type: "string" },
    { field_id: "email", type: "string" },
    { field_id: "campus", type: "string" }
  ],
  indexes: [
    { id: "campus_idx", name: "Campus", keys: ["campus"] }
  ]
}

Route Example

route is the navigation-structure building block.

Use it when:

  • the app has named destinations
  • you want menu structure to be explicit in source
  • you want navigation to be modeled as an app asset, not just ad hoc button logic

Routes do not replace components. They describe how components and destinations are presented as a navigable app.

Example routes/app.route.json5:

{
  id: "app_route",
  menuTitle: "App Route",
  menu: [
    { title: "Overview", prog: "app" },
    { title: "Students", prog: "student_list" },
    { title: "Profiles", prog: "profile_center" }
  ]
}

AI Agent Example

ai_agent is the assistant-resource building block.

Use it when:

  • the app needs a defined AI helper
  • agent configuration should live beside the rest of the app source
  • you want AI capabilities to be visible as part of the app architecture

This is especially useful when an app has:

  • a help assistant
  • a scheduling assistant
  • a support or triage assistant
  • a future copiloting feature that should be modeled early

Example ai-agents/class-helper.agent.json5:

{
  id: "class_helper",
  menuTitle: "Class Helper",
  config: {
    provider: "openai",
    model: "gpt-5"
  }
}

Root App Example

Minimal App.xu:

<meta lang="json5">
{
  menuTitle: "Hello Xuda",
  uiFramework: "@xuda.io/xuda-framework-plugin-tailwind"
}
</meta>

<template>
  <single-view>
    <section class="min-h-screen grid place-items-center p-8 text-center">
      <div class="space-y-4">
        <img class="w-20 h-20 mx-auto rounded-2xl shadow-xl" src="/favicon.ico" alt="Xuda logo">
        <h1>Hello from xuda-cli</h1>
        <p>Start with App.xu, then add feature units under the typed folders.</p>
      </div>
    </section>
  </single-view>
</template>

Client Script and Styles

Component <script> blocks are compiled into client-side mount modules.

Example:

<script>
export default function mount(ctx) {
  console.log('mounted', ctx.mount.id);
}
</script>

If the default export returns a function, that function is used as a disposer on unload.

Component <style> blocks are collected into the generated component stylesheet served by the app.

Naming and Id Rules

Rules:

  • App.xu always compiles to program id app
  • for other .xu files, the id resolves from:
  • meta.id
  • mount.id
  • file basename
  • for structured resources, the id resolves from:
  • id
  • _id
  • file basename
  • duplicate ids are not allowed

Plugins

Install plugins as npm dependencies in the app project.

Typical setup:

npm install @xuda.io/xuda-framework-plugin-tailwind
npm install @xuda.io/xuda-dbs-plugin-xuda

Typical usage:

  • @xuda.io/xuda-framework-plugin-tailwind Use it through uiFramework in component metadata
  • @xuda.io/xuda-dbs-plugin-xuda Use it by authoring table, get_data, and set_data resources

Example:

<meta lang="json5">
{
  uiFramework: "@xuda.io/xuda-framework-plugin-tailwind"
}
</meta>

Generated Runtime Docs

xuda-cli always compiles runtime docs into:

node_modules/.project_data/

Important rules:

  • the runtime reads from node_modules/.project_data/
  • you author source in .xu and .json5 files, not in generated docs
  • saving a supported source file regenerates the matching runtime JSON docs
  • legacy project_data/ or root .project_data/ can still be imported during migration, but they are not the live runtime target

You should treat node_modules/.project_data/ as generated output.

Sync and Cloud Projects

If api_key exists in xuda.config.js, you can run:

npm run sync

sync does this:

  1. verifies the app exposes the required CPI methods
  2. verifies the current API key can use those methods
  3. downloads remote docs into node_modules/.project_data/
  4. creates missing local source files from those docs
  5. recompiles local source back into node_modules/.project_data/
  6. uploads changed docs back to the cloud

Required exposed CPI methods:

  • get_app_system_api_methods
  • get_app_api_keys
  • get_project_docs
  • update_app_info_doc_to_db_couch

Important sync rule:

  • source import is non-destructive by default, so existing local source files are skipped instead of being overwritten

Recommended Workflow

For new apps:

  1. Create the app with npm create xuda-cli@latest my-app
  2. Install dependencies with npm install
  3. Keep index.html small and use it only as the page shell
  4. Build the actual app in App.xu
  5. Move feature screens into components/
  6. Put shared state into globals/
  7. Put reads and writes into data/get/ and data/set/
  8. Put tables into tables/
  9. Install plugins with npm instead of looking for a CLI plugin command
  10. Treat node_modules/.project_data/ as generated output

A Typical Xuda App Architecture

A practical Xuda app often looks like this:

  • App.xu root shell, login state, high-level navigation, main panels
  • components/ feature screens and reusable panels
  • globals/ shared identity, workspace, or preference state
  • data/get/ named loaders for tables and filtered reads
  • data/set/ named save/delete/update units
  • data/batch/ background-style or iterative processing
  • apis/ app-facing or machine-facing responses
  • scripts/ custom transformations and helpers
  • alerts/ reusable user feedback
  • tables/ data shape and indexes
  • routes/ destination map and menu structure
  • ai-agents/ future-facing or current assistant definitions

If you keep each concern in the right block, the app is usually:

  • easier to read
  • easier to debug
  • easier to reuse
  • easier to sync with cloud docs later

Example App

For a full multi-screen sample, see the published example package:

xuda-studant-app-example

That example includes:

  • login flow
  • overview dashboard
  • students screen
  • tasks screen
  • profiles screen
  • route resource
  • globals
  • get_data
  • set_data
  • batch
  • api
  • javascript
  • alert
  • ai agent
  • database-backed examples
  • Tailwind-based styling

Troubleshooting

I can run the app, but I do not see app.json

Run npm run dev from the project root. xuda-cli writes generated runtime docs when it loads the project.

Do I need both index.html and App.xu?

Yes, usually.

  • index.html is the document shell
  • App.xu is the root Xuda UI

Can I remove App.xu and start from a component under components/?

You can build reusable screens under components/, but the recommended root is still App.xu.

Where should I put business logic?

Use dedicated source units:

  • get_data for reads
  • set_data for writes
  • batch for record iteration
  • api for API-style outputs
  • javascript for custom JS logic
  • alert for messaging/feedback

Summary

Use xuda-cli like this:

  • index.html for the page shell
  • App.xu for the root app
  • typed folders for every Xuda building block
  • npm-installed plugins
  • node_modules/.project_data/ as the only live runtime doc folder
  • npm run dev for local development
  • npm run sync when api_key is configured and you want cloud sync