Creating Project Templates
Project templates let users scaffold new ICP projects with icp new. This guide covers creating custom templates for your team or the community.
Overview
icp-cli uses cargo-generate for project templating. Templates are folders or git repositories containing:
- Project files with placeholder variables
- A
cargo-generate.tomlconfiguration file
Quick Start
Minimal Template
Create a basic template:
my-template/├── cargo-generate.toml├── icp.yaml├── {{project-name}}.did└── src/ └── main.mocargo-generate.toml:
[template]name = "My ICP Template"description = "A simple ICP project template"icp.yaml:
canisters: - name: {{project-name}} recipe: type: "@dfinity/motoko" configuration: main: src/main.moFilenames with handlebar placeholders like {{project-name}}.did will be renamed with value.
Using Your Template
# From local directoryicp new my-project --path /path/to/my-template
# From Git repositoryicp new my-project --git https://github.com/user/my-templateTemplate Variables
Built-in Variables
cargo-generate provides these variables automatically:
| Variable | Description |
|---|---|
{{project-name}} | Project name (kebab-case) |
{{crate_name}} | Project name (snake_case) |
{{authors}} | Git user name |
Custom Variables
Define custom variables in cargo-generate.toml:
[template]name = "My Template"
[placeholders]include_frontend = { type = "bool", prompt = "Include frontend?", default = true }Use them in templates with Liquid syntax:
canisters:
# ... snip snip for brevity ...
{% if include_frontend %} - name: {{project-name}}-frontend recipe: type: "@dfinity/asset-canister" configuration: dir: dist {% endif %}Template Structure
Recommended Layout
my-template/├── cargo-generate.toml # Template configuration├── icp.yaml # Project manifest├── README.md # Project readme (templated)├── src/│ ├── backend/│ │ └── main.mo # Backend source│ └── frontend/ # Frontend (if applicable)│ └── index.html└── .gitignoreConfiguration File
A complete cargo-generate.toml:
[template]name = "Full Stack ICP App"description = "A complete ICP application with backend and frontend"# Exclude files from the generated projectexclude = [ ".git", "target", ".icp"]
[placeholders]backend_language = { type = "string", prompt = "Backend language?", choices = ["motoko", "rust"], default = "motoko" }include_frontend = { type = "bool", prompt = "Include frontend?", default = true }frontend_framework = { type = "string", prompt = "Frontend framework?", choices = ["vanilla", "react", "svelte"], default = "vanilla" }
# Conditional files based on selections# Ignore Rust files when Motoko is selected[conditional.'backend_language == "motoko"']ignore = ["Cargo.toml", "src/backend/lib.rs"]
# Ignore Motoko files when Rust is selected[conditional.'backend_language == "rust"']ignore = ["src/backend/main.mo"]Advanced Features
Conditional Content
Use Liquid conditionals in any file:
canisters: - name: {{project-name}} {% if backend_language == "rust" %} recipe: type: "@dfinity/rust" configuration: package: {{crate_name}} {% else %} recipe: type: "@dfinity/motoko" configuration: main: src/backend/main.mo {% endif %}Conditional Files
Ignore files based on user choices:
# Ignore frontend files when include_frontend is false[conditional.'!include_frontend']ignore = ["src/frontend/", "package.json"]Post-Generation Hooks
Run Rhai scripts after generation:
[hooks]post = ["post-generate.rhai"]Example post-generate.rhai script:
// Rename a directory based on user selectionlet backend = variable::get("backend_type");if backend == "rust" { file::rename("rust-backend", "backend");} else { file::rename("motoko-backend", "backend");}Note: Hooks execute Rhai scripts, not shell commands directly. See the cargo-generate scripting documentation for available functions.
Subfolders for Multiple Templates
Organize multiple templates in one repository:
icp-templates/├── motoko-basic/│ └── cargo-generate.toml├── rust-basic/│ └── cargo-generate.toml└── full-stack/ └── cargo-generate.tomlUse with --subfolder:
icp new my-project --git https://github.com/org/icp-templates --subfolder motoko-basicExample Templates
The default templates in github.com/dfinity/icp-cli-templates serve as good examples to follow.
To use more advanced features of cargo-generate, it is recommended you check out the book https://cargo-generate.github.io/cargo-generate/.
Testing Templates
Local Testing
Test without publishing:
# Test from local directoryicp new test-project --path ./my-template
# Verify the generated projectcd test-projecticp network start -dicp deployValidation Checklist
Before publishing, verify:
-
icp newcompletes without errors - Generated project builds:
icp build - Generated project deploys to the local network:
icp deploy - Variables are substituted correctly
- Conditional content works as expected
- README is helpful and accurate
Publishing Templates
GitHub Repository
- Push your template to GitHub
- Users can reference it directly:
icp new my-project --git https://github.com/username/my-templateWith Tags/Branches
Pin to specific versions:
# Use a tagicp new my-project --git https://github.com/user/template --tag v1.0.0
# Use a branchicp new my-project --git https://github.com/user/template --branch stableOfficial Templates
The default templates are in github.com/dfinity/icp-cli-templates. To contribute:
- Fork the repository
- Add your template as a subfolder
- Submit a pull request
Next Steps
- Tutorial — Use templates to create projects
- Creating Recipes — Create reusable build configurations