Getting Started
July 5, 2026 · View on GitHub
This guide will help you set up Coi and create your first component.
Installation
Prerequisites
Coi requires WebCC to be installed. The build script will automatically initialize and build the WebCC submodule if it is not found on your system.
Building from Source
To build the compiler and the toolchain, clone the repository and run the build script:
git clone https://github.com/coi/coi.git
cd coi
./build.sh
This will:
- Initialize the WebCC submodule (if needed)
- Build the WebCC toolchain
- Generate type definition files (defs/web/*.d.coi) from WebCC schema
- Build the Coi compiler
Build Options
The build script supports these options:
./build.sh --rebuild-schema # Force regenerate defs/web/*.d.coi from WebCC schema
./build.sh --rebuild-webcc # Force rebuild the WebCC toolchain
./build.sh --help # Show all available options
Type Definition System
Coi uses .d.coi definition files for type information:
defs/core/— Built-in types (int, string, bool, array, etc.) - source-controlleddefs/web/— Web platform APIs auto-generated from WebCC schema - gitignored
The build system automatically detects when deps/webcc/schema.def changes:
- Running
./build.shchecks if WebCC's schema.def was modified - If changed, WebCC rebuilds automatically
- Coi regenerates
defs/web/*.d.coito match the new schema
This means you can simply run ./build.sh after editing schema.def and everything cascades correctly.
Use --rebuild-schema to force regeneration even when no changes are detected.
CLI Commands
The Coi CLI provides commands for creating, building, and running projects.
coi init
Create a new Coi project from template:
coi init my-app
This creates a new directory with a complete project structure:
my-app/
├── assets/
│ └── images/
├── src/
│ ├── App.coi
│ ├── layout/
│ │ ├── Footer.coi
│ │ └── NavBar.coi
│ ├── pages/
│ │ ├── About.coi
│ │ └── Home.coi
│ └── ui/
│ └── Button.coi
├── styles/
│ └── reset.css
└── README.md
Creating a Package
To create a reusable component package instead of an app:
coi init my-pkg --pkg
This creates a package structure with Mod.coi as the entry point and a package.json template for publishing.
See Package Manager for the full workflow on creating and publishing packages.
If no name is provided, you'll be prompted to enter one:
coi init
# Project name: my-app
Project names must start with a letter or underscore and contain only letters, numbers, hyphens, and underscores.
coi build
Build the project in the current directory:
cd my-app
coi build
This compiles src/App.coi and outputs to dist/:
dist/index.html— Entry HTML filedist/app.js— JavaScript runtimedist/app.wasm— WebAssembly binarydist/app.css— Generated CSS bundle
Assets from the assets/ folder are automatically copied to dist/assets/.
coi dev
Build and start a local development server with hot reloading:
coi dev
This builds the project and starts a server at http://localhost:8000. The server automatically watches for changes to:
.coifiles insrc/- Files in
assets/(images, fonts, etc.) .cssfiles instyles/
When you save any watched file, the project rebuilds automatically and your browser refreshes with the latest changes.
Disable Hot Reloading
If you need to disable hot reloading (for debugging build issues or testing manual workflows):
coi dev --no-watch
This builds the project once and starts the dev server without file watching. To see changes, stop the server and run coi dev --no-watch again.
Press Ctrl+C to stop the dev server.
Direct Compilation
Compile a single .coi file directly:
coi App.coi --out ./dist
Options
| Option | Description |
|---|---|
--out, -o <dir> | Output directory |
--cc-only | Generate C++ only, skip WASM compilation |
--keep-cc | Keep generated C++ files for debugging |
To keep the intermediate C++ file:
coi App.coi --out ./dist --keep-cc
This also generates dist/App.cc so you can inspect the generated C++ code.
Package Management
Coi has a built-in package manager for adding community packages:
coi add @coi/supabase # Add a package
coi install # Install from coi.lock
Then import it:
import "@coi/supabase";
See Package Manager for the full workflow.
Your First Component
Create a file called App.coi:
component App {
mut int count = 0;
def increment() : void {
count += 1;
}
style {
.container {
padding: 20px;
font-family: system-ui;
}
button {
padding: 8px 16px;
cursor: pointer;
}
}
view {
<div class="container">
<h1>Count: {count}</h1>
<button onclick={increment}>+1</button>
</div>
}
}
app { root = App; }
App Configuration
The app {} block configures your application. Here are all available properties:
app {
root = App; // Required: Root component
title = "My App"; // Page title (<title> tag)
description = "A description for SEO"; // Meta description
lang = "en"; // HTML lang attribute (default: "en")
base = "/"; // Deploy base path (default: "/")
}
| Property | Type | Required | Description |
|---|---|---|---|
root | Component | Yes | The root component to render |
title | String | No | Sets the page <title> tag |
description | String | No | Sets <meta name="description"> for SEO |
lang | String | No | Sets the <html lang=""> attribute (default: "en") |
base | String | No | Deploy base path, emitted as <base href=""> (default: "/") |
Note: If you have a styles/ folder at the project root (next to src/), all .css files in it are automatically bundled into app.css.
Deploying under a subpath: Assets (app.js, app.css) and client-side routes resolve against the base path. When your site is served from a subpath rather than the domain root, for example a GitHub project page at https://user.github.io/repo/, set base to that subpath so assets load correctly:
app {
root = App;
base = "/repo/"; // matches https://user.github.io/repo/
}
Leave base as "/" (the default) for root deploys and local development.
For client-side routing, use the router {} block inside your root component. See Components for details.
Compile and run:
coi App.coi --out ./dist
cd dist && python3 -m http.server
Project Structure
For larger projects, organize your code into multiple files:
my-app/
├── src/
│ ├── App.coi
│ ├── components/
│ │ ├── Button.coi
│ │ └── Card.coi
│ └── layout/
│ ├── Header.coi
│ └── Footer.coi
├── dist/ # Generated output
└── build.sh
Imports
Coi uses a strict, explicit import system.
// Local imports (relative to current file)
import "components/Button.coi";
import "layout/Header.coi";
// Package imports (from .coi/pkgs/)
import "@coi/supabase"; // resolves to .coi/pkgs/coi-lang/supabase/Mod.coi
import "@acme/utils/Button.coi"; // resolves to .coi/pkgs/acme/utils/Button.coi
Import Rules
- Relative Paths: Local imports are relative to the current file.
- Package Imports: Paths starting with
@resolve to.coi/pkgs/. Use scoped names like@scope/name. - Explicit Only: There are no "transitive imports". If
AimportsB, andBimportsC,Acannot useCunless it importsCdirectly. - Visibility: You can only use components that are marked with
pubif they are in a different module.
Modules
You can organize files into named modules using the module keyword at the top of the file. Module names must start with an uppercase letter:
// src/ui/Button.coi
module TurboUI;
pub component Button { ... }
- Same Module: Can access
Buttondirectly after import. - Different Module: Must use fully qualified name
<TurboUI::Button />.
Getting Help
Getting stuck or need help? Join the Coi Discord community for fast support and discussions, or open an issue on GitHub for bugs and feature requests.
Next Steps
- Language Guide — Types, control flow, operators
- Components — Component syntax, lifecycle, props
- Styling — Scoped and global CSS
- View Syntax — JSX-like templates, conditionals, loops
- Platform APIs — Canvas, Storage, Audio, and more