Quick Start

This guide walks you from zero to a running Swift Web project. The core flow takes about 5 minutes (steps 1–5); the last three steps (API / site icons / custom icons) are on-demand and don't block getting started.

First time with Swift Web? You don't need to read any architecture docs first — just follow along. Every step explains what to do, why, and what you should see afterwards.

Prerequisites

Make sure you have these three things:

RequirementVersionHow to check / install
Node.js^22.22.3 || ^24.15.0 || >=26.0.0node -v; manage versions with nvm
pnpm11+pnpm -v; install via npm install -g pnpm
Private registry accessinternal npm registrythe install script in step 1 configures it automatically

For development and builds, use Node.js 26.13.0 LTS and pnpm 12.16.1. Development machines, CI jobs, and servers can use different Node versions. Check the installed dependencies’ engines on servers; Nuxt 4.6.4 requires at least 24.15.0 on the Node 24 line, so servers do not need to follow the development machine to Node 26.

Why pnpm 11+: the starter's engines.pnpm: ">=11" is a hard gate — older versions fail pnpm install with ERR_PNPM_UNSUPPORTED_ENGINE. This is deliberate (pnpm 11 ships supply-chain safety policies by default).

Step 1: Install the CLI

sw is the Swift Web scaffolding command — install once, use globally.

macOS / Linux

curl -sSL https://sw.djatm.cloud/install.sh | bash

One command does it all: checks Node.js → installs pnpm if missing and verifies it → initializes the global command directory and the script's PATH → configures the private registry → installs @swift-web/cli globally.

If Corepack reports a missing pnpm.cjs in its cache, reinstall pnpm with the official pnpm installer, then open a new terminal and retry. The CLI installer preserves the original error and stops if pnpm or pnpm setup fails. After a successful install, open a new terminal to load the updated PATH if your existing terminal cannot find sw.

Windows (PowerShell)

irm https://sw.djatm.cloud/install.ps1 | iex

Windows requires Node.js (^22.22.3 || ^24.15.0 || >=26.0.0) installed first; PowerShell 5.1+ (built into Windows 10/11).

Manual install

Prefer doing it by hand? Two steps. First configure the private npm registry (once per machine):

macOS / Linux:

echo '@swift-web:registry=https://npm.djatm.cloud/' >> ~/.npmrc

Windows (PowerShell):

Add-Content "$env:USERPROFILE\.npmrc" "`n@swift-web:registry=https://npm.djatm.cloud/"

Then install globally:

pnpm add -g @swift-web/cli

Upgraded on release day and got the previous version? pnpm ≥11 ships a 24-hour "cooling period" (minimumReleaseAge): a version published today cannot be resolved for a day, so pnpm add -g silently installs the previous one. The exemption in a project's pnpm-workspace.yaml does not apply to global installs — add minimumReleaseAgeExclude[]=@swift-web/* to your user-level ~/.npmrc (the one-line installer already writes it), or pin the version: pnpm add -g @swift-web/cli@<version>.

Verify

sw -v
# Prints something like: 0.8.x (depends on the installed @swift-web/cli)

A version number means you're good.

Step 2: Create the project

Initialize with sw create. For repository naming consistency it only accepts two formats:

YC format (mandatory internal naming)

sw create YC001-WEB-ProjectTitle

Rule: YC + number + -WEB- + a Chinese or English title. Case-insensitive.

Example:

sw create YC117-WEB-CinemaManager
# Directory: yc117_web/
# project name: yc117
# project title: CinemaManager

The CLI extracts name and title from the input — no extra prompts.

Initialize in the current directory (.)

If you've already created a directory or want to scaffold at the repo root, pass .:

mkdir my-project && cd my-project
sw create .

The CLI then asks for title and description interactively; project.name is taken from the directory name.

Plain string names are rejected (e.g. sw create my-project) — every internal project must be identifiable from its repo name.

Interactive wizard

During creation the CLI asks about the following capabilities (unsure? just press Enter for defaults — everything can be changed later with sw config set):

QuestionDefaultEffect
Theme modelightlight / dark / system (follow OS) / toggle (user-switchable, mounts useTheme() + SwThemeSwitch; then asks tri-state / two-state)
Enable i18nNointegrates @nuxtjs/i18n; multi-select languages
Enable PWANoinjects Service Worker + manifest
Enable WebSocketNoregisters the global $ws client
Enable Tailwind CSS v4Noinstalls deps + wires the Vite plugin
Run pnpm install nowYesinstalls dependencies

Choices are written to swift.config.json and synced into nuxt.config.ts / i18n/locales/ etc.

Non-interactive mode (CI / scripts)

sw create YC001-WEB-ProjectTitle --yes        # skip prompts, all defaults
sw create YC001-WEB-ProjectTitle --yes --json # plus structured JSON output

Step 3: Enter the project directory

cd yc001_web   # directory name derived from the YC number

In interactive mode the CLI already asked "install dependencies now?" — if you said Yes, no manual pnpm install needed.

Step 4: Configure environment variables

sw create already copied .env.dev.example to .env — just edit it (no cp needed):

# Edit .env; at minimum fill in:
NUXT_PUBLIC_SITE_URL=http://localhost:3000
NUXT_PUBLIC_API_BASE=https://api.your-domain.com
  • NUXT_PUBLIC_SITE_URL: your site's own address — http://localhost:3000 is fine for local dev. ⚠️ It has a second effect: setting it makes canonical / og:url / sitemap converge on that domain instead of following the domain the visitor actually used. No impact for single-domain sites; multi-domain (anti-block mirror) sites should leave it empty so each mirror self-references and gets indexed independently (see the SEO guide)
  • NUXT_PUBLIC_API_BASE: the backend API address — no backend yet? No problem, leave it for now; step 6 covers developing without one

Other fields (dev-time tooling such as YAPI codegen) are optional; every line in the example file is commented. Cookie names / cache prefix / AES keys are project constants and no longer live in .env — they ship as real values in nuxt.config.ts runtimeConfig defaults (the build artifact is self-contained; zero required env at runtime; a same-named NUXT_PUBLIC_* process env can still override temporarily).

.env is gitignored; share secrets through a secure channel.

Step 5: Start the dev server

pnpm dev

You should see:

  ➜  Local:   http://localhost:3000/
  ➜  Network: http://0.0.0.0:3000/

Open http://localhost:3000 — the starter homepage means you're up. 🎉


The project now runs. The next three steps are common post-setup tasks — none of them blocks development:

StepWhenIf skipped
Step 6 · Initialize APIsonce backend + YAPI docs are readyno real API calls; use systemInfo: false to build static pages first
Step 7 · Generate site iconsonce you have the product logo; before launch is finedefault favicon in the tab, nothing breaks
Step 8 · Custom SVG iconswhenever a page needs business iconsno impact

Step 6: Initialize APIs (once the backend is ready)

API types and request functions are generated from API docs (YAPI or an OpenAPI 3 spec) — never handwritten.

Backend ready

  1. The doc-source config lives in two places (ask your backend teammate for the values) — non-secrets go into swiftConfig.apiDocs in nuxt.config.ts (committed, travels with the repo; the template ships a placeholder block), the secret stays in .env:
    // nuxt.config.ts → swiftConfig
    apiDocs: { source: 'yapi', url: 'https://your-yapi-host.com', project: 123 },
    
    # .env — token only
    API_DOCS_TOKEN=xxxxxxxx
    

    OpenAPI works the same way: source = openapi, url = the spec URL; no token needed unless the spec sits behind auth.
  2. Generate:
    sw run generate-api
    

    Afterwards app/api/index.ts, app/api/modules/*.ts and app/api/types/*.ts are a complete snapshot of the backend; call api.module.method() in pages. Re-run whenever the backend changes.

Backend / YAPI not ready: build static pages first

Early in a project it's normal for the backend or YAPI docs to not exist yet. To develop static pages that call no APIs, temporarily set systemInfo to false in the swiftConfig block of nuxt.config.ts:

// nuxt.config.ts → swiftConfig
systemInfo: false,   // temporary: skip fetching /system/info at startup

What this is: on startup the framework fetches /system/info from the backend (site title, image decryption key, menus, tracking config, …). With no backend the request fails and spams errors — turning it off keeps static-page development clean.

  • While off, features that depend on /system/info (.bnc image decryption, backend menus, tracking config, …) get no data; pure static development is unaffected;
  • If the login backend is also absent, the dev console may still show auth request errors — harmless for static pages; disable swiftConfig.auth as well if it bothers you (see framework options);
  • Remember to revert (remove the systemInfo: false line) once the backend is up, then run sw run generate-api above.

This step produces the browser favicon and PWA app icons. It does not need to happen right after project creation — do it when the logo arrives; before launch is fine.

  1. Put the logo source in app/assets/, one of:
    • icon.svg (recommended): vector; you can even embed @media (prefers-color-scheme: dark) inside the SVG so the favicon follows the system theme
    • icon.png: when the logo only exists as a bitmap (no theme switching)

    If both exist, SVG wins.
  2. Generate:
    sw run generate-icons
    

    One command outputs the full set:
    OutputPurpose
    public/favicon.svgmodern tab icon (SVG input only)
    public/favicon.png (48×48)PNG fallback for the tab
    public/icons/apple-touch-icon.png (180×180)iOS Safari "Add to Home Screen"
    public/icons/icon-192.png / icon-512.pngPWA manifest icons

Safe to run without PWA enabled — the PWA-sized icons just sit in public/icons/ with zero side effects; when PWA is enabled later, @vite-pwa/nuxt takes over the manifest and references them automatically.

Step 8: Custom SVG icons (add as you go)

When a page needs business icons (navigation, buttons, status marks…):

  1. Drop the SVG into app/assets/icons/ — the filename becomes the icon name (e.g. game.svg);
  2. It just works in dev — a built-in Vite plugin watches the directory, injects new/changed SVGs into app/components/AppIcon.vue (between the <!-- ICONS:START --> markers) and hot-reloads. Usually no command needed;
  3. Use it in templates:
    <AppIcon name="game" />
    <AppIcon name="game" :width="32" color="var(--primary)" />
    

Only in rare cases (e.g. bulk-adding icons while the dev server is off) run sw run inject-icons manually.

Know the difference: business SVG icons go through AppIcon (this section); generic UI icons (arrows/close) use an icon library like @phosphor-icons/vue; bitmap content images use SwImage. See Dev Standards · image asset selection.

For AI: built-in MCP (zero config)

If you develop with Claude Code / Cursor or other AI coding tools, this section doubles your throughput — and requires no setup: the starter ships the MCP config (which simply launches sw --mcp) — Claude Code reads .mcp.json, Cursor reads .cursor/mcp.json, both included with identical content — so AI tools connect to the Swift Web MCP server the moment they open the project.

What it can do

GroupMCP toolsPurpose
Scaffoldingsw_add_page / sw_add_component / sw_add_store / sw_add_apicreate pages / components / stores / API modules from convention templates
Project configsw_config_get / sw_config_set / sw_config_syncread/write swift.config.json with nuxt.config.ts kept in sync
Script runningsw_runlist / run any package.json script (generate-api / generate-icons / test…)
Docs lookupsw_framework_docsquery docs of the installed framework version (rich JSDoc + examples + props + related gotchas); omit the name to list the catalog
API lookupsw_project_apislist the project's backend APIs (parses the generated app/api layer) — check which endpoint to call before wiring page logic
Project infosw_info / sw_version / sw_upgrade / sw_createproject metadata / CLI version / upgrade check / project creation

Natural-language examples

Once connected you don't memorize commands — just talk:

You: create an "About us" page AI: calls sw_add_page to create app/pages/about-us.vue (with defineRouteRules / useSeo boilerplate), then fills in content.

You: which framework capabilities are enabled in this project? AI: calls sw_config_get and reports the theme / i18n / pwa / tracker switches.

You: what props does SwImage have? How do encrypted images work? AI: calls sw_framework_docs("SwImage") and answers from the docs of the exact version you installed — more reliable than the docs site (no version drift).

You: the backend YAPI changed, sync the APIs AI: calls sw_run("generate-api") and reports the diff.

You: turn on i18n AI: calls sw_config_set("framework.i18n.enabled", true) — uncomments nuxt.config blocks and scaffolds locale files automatically.

You: create a store for user favorites AI: calls sw_add_store to generate app/store/user-favorites.ts (standard Pinia template), then fills in state / actions as needed.

You: is there a new framework release? Upgrade it for me AI: calls sw_upgrade to check the private registry and run the upgrade (under the hood it rewrites the framework range in package.json, then runs pnpm install --lockfile-only and pnpm install --frozen-lockfile), then reminds you to restart the dev server.

You: add a "Ranking" page — hourly freshness is fine AI: calls sw_add_page to create app/pages/ranking.vue, then changes the boilerplate defineRouteRules to { isr: 3600 } — the tool scaffolds, and the AI fills in judgment calls like the rendering strategy from context.

Why this matters: through MCP the AI gets structured, versioned facts (instead of guessing APIs from training memory); combined with the project's CLAUDE.md conventions, AI-generated code naturally follows the framework's rules. This is the "AI-friendly" design goal made real.

These are just the common cases — see CLI reference · AI Toolchain (MCP) for all 15 MCP tools and every configurable field.

Generated project structure

sw create pulls the latest starter from the verdaccio registry (npm.djatm.cloud) via npm pack @swift-web/starter and replaces placeholders. Current skeleton (evolves with the starter):

yc001_web/
├── app/
│   ├── components/         business components (AppHeader / AppFooter / LoginForm / TabBar …)
│   ├── pages/              routed pages (index / login / register / mine / ...)
│   ├── layouts/            layouts (default.vue / empty.vue)
│   ├── composables/        business composables
│   ├── store/              business Pinia stores
│   ├── api/                API modules (generated in step 6)
│   ├── assets/             CSS tokens & global styles, icon source (icon.svg), icons/ business SVGs
│   ├── middleware/         business route guards
│   ├── plugins/            business plugins
│   ├── utils/              business utilities
│   ├── constants/          business constants
│   ├── types/              business types
│   └── app.vue / error.vue app entry / global error page
├── public/                 static assets (images / icons / favicon — step 7 outputs)
├── i18n/                   translations (only when i18n enabled, trimmed to chosen locales)
├── server/                 Nitro server APIs
├── scripts/                build scripts (thin wrappers: inject-icons / optimize-images / generate-api …)
├── tests/                  tests (unit / nuxt + e2e Playwright)
├── config/                 runtime config (env validation etc.)
├── types/                  global type declarations
├── patches/                pnpm patches
├── deploy/                 production deploy script deploy-prod.sh (the `pnpm deploy:prod` entry)
├── .env.dev.example        env example (committed; production uses .env.prod.example)
├── .env                    local env (auto-created by sw create, gitignored)
├── .mcp.json               MCP config (Claude Code; Cursor reads .cursor/mcp.json, same content)
├── .gitlab-ci.yml          CI checks (types / lint / unit coverage / build)
├── CLAUDE.md               AI collaboration rules (plus .cursor/ .github/ variants)
├── nuxt.config.ts          Nuxt config (with swiftConfig — step 6's systemInfo lives here)
├── swift.config.json       Swift Web project metadata
├── tsconfig.json           TypeScript config (plus eslint / stylelint / prettier / playwright / vitest configs)
└── ecosystem.config.cjs    PM2 production process config (plus ecosystem.dev.config.cjs for the test box; shared test machines need a unique PORT)

swift.config.json

The Swift Web project file — sw commands use it to recognize a Swift Web project. Default structure after --yes creation:

{
  "project": {
    "name": "yc001",
    "type": "web",
    "title": "ProjectTitle",
    "description": ""
  },
  "framework": {
    "theme": { "mode": "light" },
    "i18n": { "enabled": false, "defaultLocale": "zh-CN", "locales": ["zh-CN"] },
    "pwa": false,
    "websocket": false,
    "errorHandler": false,
    "tracker": { "enabled": true, "autoPageView": true }
  },
  "features": {
    "tailwind": false
  }
}

Field notes:

  • project.name — npm package name prefix (lowercase), derived from the YC number, e.g. yc001
  • project.type — locked to 'web'; sw config set project.type returns TYPE_LOCKED
  • project.title / description — used for nuxt.config.ts title / meta
  • framework.* — capability switches, synced with nuxt.config.ts's swiftConfig via sw config set
  • framework.tracker — tracking foundation switch (on by default, wizard doesn't ask); disable via sw config set framework.tracker.enabled false
  • features.tailwind — Tailwind v4 switch; sw config set features.tailwind true installs deps, updates configs, creates tailwind.css

Common commands

CommandDescription
pnpm devdev server (HMR)
pnpm buildproduction build
pnpm analyzebuild + bundle size analysis
pnpm previewpreview the production build
pnpm generatestatic site generation (SSG)
pnpm cleanremove the .nuxt cache
pnpm generate-apigenerate API types & functions from YAPI (step 6)
pnpm generate-iconsgenerate favicon + PWA icons from app/assets/icon.svg (step 7)
pnpm optimize-imagesbatch-compress public/images/ into multiple formats
pnpm inject-iconsmanually inject assets/icons/ SVGs into AppIcon (auto in dev; rarely needed)
pnpm linteslint + stylelint (with --fix)
pnpm type-checkNuxt TypeScript check
pnpm testVitest unit tests (once)
pnpm test:watchVitest watch mode
pnpm test:coverageVitest coverage
pnpm test:e2ePlaywright end-to-end tests
pnpm test:e2e:uiPlaywright in UI mode

Every pnpm <script> can equivalently be sw run <script> (the unified entry for AI tools / terminals). Run sw run at the project root to list all scripts with one-line descriptions.

Next steps

  • Read the CLI reference for all sw commands (incl. sw run; the starter ships MCP configs so AI toolchains work out of the box)
  • Read the Dev Standards for team conventions
  • Read Framework Config for all swiftConfig options