This guide will help you set up Coi and create your first component.
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.
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.shThis 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
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 optionsCoi 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.
The Coi CLI provides commands for creating, building, and running projects.
Create a new Coi project from template:
coi init my-appThis 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
To create a reusable component package instead of an app:
coi init my-pkg --pkgThis 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-appProject names must start with a letter or underscore and contain only letters, numbers, hyphens, and underscores.
Build the project in the current directory:
cd my-app
coi buildThis 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/.
Build and start a local development server with hot reloading:
coi devThis 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.
If you need to disable hot reloading (for debugging build issues or testing manual workflows):
coi dev --no-watchThis 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.
Compile a single .coi file directly:
coi App.coi --out ./dist| 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-ccThis also generates dist/App.cc so you can inspect the generated C++ code.
Coi has a built-in package manager for adding community packages:
coi add @coi/supabase # Add a package
coi install # Install from coi.lockThen import it:
import "@coi/supabase";See Package Manager for the full workflow.
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; }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")
}| 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") |
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.
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.serverFor 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
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- 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.
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 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.
- 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