Get started
Boot a local Sui stack, fund accounts, publish packages, wire your app, and generate bindings — all from one TypeScript config.
Spin up everything your Sui app needs to run locally from one TypeScript file. A single
devstack.config.ts boots a local network, funds named accounts, publishes your Move packages,
wires your dev server and a browser dev wallet, and generates bindings plus a runtime config your
app imports — so pnpm dev gives you a working chain, funded wallets, and your published contracts.
You describe the stack as a list of members — sui(), account(), localPackage(), wallet(),
walrus(), seal(), deepbook(), action(), and dashboard() — and devstack stands them up.
Install
Start a new app with the create tool:
pnpm create @mysten-incubation/devstack-app@latest my-app
cd my-app
pnpm devAdd devstack to an existing app — install the devstack package, the dev wallet used by the generated browser bindings, and the shared tsconfig package:
pnpm add @mysten-incubation/devstack @mysten-incubation/dev-wallet @mysten/signers
pnpm add -D @mysten-incubation/tsconfigConfigure the stack
Add a devstack.config.ts at the app root:
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
defineDevstack,
account,
HOST_SERVICE_PORT_TOKEN,
hostService,
localPackage,
sui,
wallet,
} from '@mysten-incubation/devstack';
const HERE = dirname(fileURLToPath(import.meta.url));
const DEV_PORT = 5173;
export const localnet = sui();
export const publisher = account('publisher');
export const alice = account('alice');
export const bob = account('bob');
export const hello = localPackage('hello', {
sourcePath: resolve(HERE, 'move/hello'),
publisher,
});
export const devWallet = wallet({
accounts: [publisher, alice, bob],
});
export const app = hostService({
name: 'app',
script: `pnpm exec vite --host 127.0.0.1 --strictPort --port ${HOST_SERVICE_PORT_TOKEN}`,
cwd: HERE,
port: DEV_PORT,
ready: { kind: 'http' },
after: [hello, devWallet] as const,
});
export default defineDevstack({
members: [localnet, app],
stackName: 'main',
});Wire the app
Add a Vite config. devstackVitePlugin() wires the @generated alias so your app can import the
generated tree, and passes the allocated port through to the hostService command.
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { devstackVitePlugin } from '@mysten-incubation/devstack/vite';
export default defineConfig({ plugins: [react(), devstackVitePlugin()] });Import the generated files through the @generated alias and wire dapp-kit from
@generated/config.js. The typed config object gives you config.networkNames,
config.defaultNetwork, and config.forNetwork(net). In dev, the dev wallet is added to the page
automatically and dApp Kit discovers it via wallet-standard; a production build carries no dev
wallet.
import { createDAppKit } from '@mysten/dapp-kit-react';
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { config } from '@generated/config.js';
export const dAppKit = createDAppKit({
networks: [...config.networkNames],
defaultNetwork: config.defaultNetwork,
autoConnect: import.meta.env.DEV,
createClient(network) {
// `config.forNetwork(net)` returns the resolved deployment for the
// selected network, so a runtime `switchNetwork` flips ids in lockstep.
const net = config.forNetwork(network);
return new SuiGrpcClient({
network,
baseUrl: net.rpc,
// Generated bindings default `package` to `@local/<name>`;
// `mvrOverrides` points each name at its deployed id.
mvr: { overrides: net.mvrOverrides },
});
},
});See Config & deployments for the full config surface and how the deployment loader resolves ids.
First boot
Generate the bindings your app builds against:
pnpm devstack codegenThis writes the src/generated tree — typed Move bindings and the runtime config your app imports —
from your Move source. It needs no running stack; run it whenever the Move source changes.
Then run the stack:
pnpm devstack upReconcile from another shell or CI:
pnpm devstack applyIf devstack up is already running for this stack, apply waits on it; otherwise it runs the same
setup once and exits. Use the generated files from src/generated, and treat .devstack/ as
runtime state only.
Core flow
- Put stack members in
devstack.config.ts. Include your dev server as ahostService(...)member when the stack backs a browser app. - Run
devstack codegento write the bindings and runtime config undersrc/generated— the tree your app builds against. Rerun it when the Move source changes. - Run
devstack upto start the stack and keep it running, ordevstack applyto reconcile from another shell or CI. - Import the generated files from
src/generated; runtime state, snapshots, and logs stay under.devstack/stacks/<stack>. - Pull in the Vitest, Playwright, Vite, and runtime helpers from their subpaths when you need them
—
@mysten-incubation/devstack/vitest,/playwright,/vite, and/runtime.
Where to go next
- Configure your stack for sui, accounts, packages, and funding.
- Walrus, Seal, and DeepBook for the local and known stack services.
- Codegen & deployments for generated bindings and the deployment config.
- Testing for unit, e2e, and browser tests.
- Live networks for running against public networks.
- Forking for mirroring a real chain at a checkpoint.
- Going to production for the prod build and dapp-kit handoff.