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:
| Requirement | Version | How to check / install |
|---|---|---|
| Node.js | ^22.22.3 || ^24.15.0 || >=26.0.0 | node -v; manage versions with nvm |
| pnpm | 11+ | pnpm -v; install via npm install -g pnpm |
| Private registry access | internal npm registry | the 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’
engineson 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 failpnpm installwithERR_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, sopnpm add -gsilently installs the previous one. The exemption in a project'spnpm-workspace.yamldoes not apply to global installs — addminimumReleaseAgeExclude[]=@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):
| Question | Default | Effect |
|---|---|---|
| Theme mode | light | light / dark / system (follow OS) / toggle (user-switchable, mounts useTheme() + SwThemeSwitch; then asks tri-state / two-state) |
| Enable i18n | No | integrates @nuxtjs/i18n; multi-select languages |
| Enable PWA | No | injects Service Worker + manifest |
| Enable WebSocket | No | registers the global $ws client |
| Enable Tailwind CSS v4 | No | installs deps + wires the Vite plugin |
| Run pnpm install now | Yes | installs 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:3000is 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).
.envis 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:
| Step | When | If skipped |
|---|---|---|
| Step 6 · Initialize APIs | once backend + YAPI docs are ready | no real API calls; use systemInfo: false to build static pages first |
| Step 7 · Generate site icons | once you have the product logo; before launch is fine | default favicon in the tab, nothing breaks |
| Step 8 · Custom SVG icons | whenever a page needs business icons | no 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
- The doc-source config lives in two places (ask your backend teammate for the values) — non-secrets go into
swiftConfig.apiDocsinnuxt.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. - Generate:
sw run generate-api
Afterwardsapp/api/index.ts,app/api/modules/*.tsandapp/api/types/*.tsare a complete snapshot of the backend; callapi.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(.bncimage 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.authas well if it bothers you (see framework options);- Remember to revert (remove the
systemInfo: falseline) once the backend is up, then runsw run generate-apiabove.
Step 7: Generate site icons (once you have a logo)
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.
- 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 themeicon.png: when the logo only exists as a bitmap (no theme switching)
If both exist, SVG wins. - Generate:
sw run generate-icons
One command outputs the full set:Output Purpose 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/nuxttakes 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…):
- Drop the SVG into
app/assets/icons/— the filename becomes the icon name (e.g.game.svg); - 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; - 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-iconsmanually.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 useSwImage. 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
| Group | MCP tools | Purpose |
|---|---|---|
| Scaffolding | sw_add_page / sw_add_component / sw_add_store / sw_add_api | create pages / components / stores / API modules from convention templates |
| Project config | sw_config_get / sw_config_set / sw_config_sync | read/write swift.config.json with nuxt.config.ts kept in sync |
| Script running | sw_run | list / run any package.json script (generate-api / generate-icons / test…) |
| Docs lookup | sw_framework_docs | query docs of the installed framework version (rich JSDoc + examples + props + related gotchas); omit the name to list the catalog |
| API lookup | sw_project_apis | list the project's backend APIs (parses the generated app/api layer) — check which endpoint to call before wiring page logic |
| Project info | sw_info / sw_version / sw_upgrade / sw_create | project 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_pageto createapp/pages/about-us.vue(withdefineRouteRules/useSeoboilerplate), then fills in content.
You: which framework capabilities are enabled in this project? AI: calls
sw_config_getand 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_storeto generateapp/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_upgradeto check the private registry and run the upgrade (under the hood it rewrites the framework range in package.json, then runspnpm install --lockfile-onlyandpnpm install --frozen-lockfile), then reminds you to restart the dev server.
You: add a "Ranking" page — hourly freshness is fine AI: calls
sw_add_pageto createapp/pages/ranking.vue, then changes the boilerplatedefineRouteRulesto{ 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.mdconventions, 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.yc001project.type— locked to'web';sw config set project.typereturnsTYPE_LOCKEDproject.title/description— used fornuxt.config.tstitle / metaframework.*— capability switches, synced withnuxt.config.ts'sswiftConfigviasw config setframework.tracker— tracking foundation switch (on by default, wizard doesn't ask); disable viasw config set framework.tracker.enabled falsefeatures.tailwind— Tailwind v4 switch;sw config set features.tailwind trueinstalls deps, updates configs, createstailwind.css
Common commands
| Command | Description |
|---|---|
pnpm dev | dev server (HMR) |
pnpm build | production build |
pnpm analyze | build + bundle size analysis |
pnpm preview | preview the production build |
pnpm generate | static site generation (SSG) |
pnpm clean | remove the .nuxt cache |
pnpm generate-api | generate API types & functions from YAPI (step 6) |
pnpm generate-icons | generate favicon + PWA icons from app/assets/icon.svg (step 7) |
pnpm optimize-images | batch-compress public/images/ into multiple formats |
pnpm inject-icons | manually inject assets/icons/ SVGs into AppIcon (auto in dev; rarely needed) |
pnpm lint | eslint + stylelint (with --fix) |
pnpm type-check | Nuxt TypeScript check |
pnpm test | Vitest unit tests (once) |
pnpm test:watch | Vitest watch mode |
pnpm test:coverage | Vitest coverage |
pnpm test:e2e | Playwright end-to-end tests |
pnpm test:e2e:ui | Playwright in UI mode |
Every
pnpm <script>can equivalently besw run <script>(the unified entry for AI tools / terminals). Runsw runat the project root to list all scripts with one-line descriptions.
Next steps
- Read the CLI reference for all
swcommands (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
swiftConfigoptions