| title | Creating Project Templates |
|---|---|
| description | Create custom project templates for icp new using cargo-generate to scaffold new ICP projects. |
Project templates let users scaffold new ICP projects with icp new. This guide covers creating custom templates for your team or the community.
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
Create a basic template:
my-template/
├── cargo-generate.toml
├── icp.yaml
├── {{project-name}}.did
└── src/
└── main.mo
cargo-generate.toml:
[template]
name = "My ICP Template"
description = "A simple ICP project template"icp.yaml:
canisters:
- name: {{project-name}}
recipe:
type: "@dfinity/motoko@v4.0.0"
configuration:
main: src/main.moFilenames with handlebar placeholders like {{project-name}}.did will be renamed with value.
# From local directory
icp new my-project --path /path/to/my-template
# From Git repository
icp new my-project --git https://github.com/user/my-templatecargo-generate provides these variables automatically:
| Variable | Description |
|---|---|
{{project-name}} |
Project name (kebab-case) |
{{crate_name}} |
Project name (snake_case) |
{{authors}} |
Git user name |
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:
# icp.yaml
canisters:
# ... snip snip for brevity ...
{% if include_frontend %}
- name: {{project-name}}-frontend
recipe:
type: "@dfinity/asset-canister@v2.1.0"
configuration:
dir: dist
{% endif %}
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
└── .gitignore
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 project
exclude = [
".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"]Use Liquid conditionals in any file:
# icp.yaml
canisters:
- name: {{project-name}}
{% if backend_language == "rust" %}
recipe:
type: "@dfinity/rust@v3.0.0"
configuration:
package: {{crate_name}}
{% else %}
recipe:
type: "@dfinity/motoko@v4.0.0"
configuration:
main: src/backend/main.mo
{% endif %}Ignore files based on user choices:
# cargo-generate.toml
# Ignore frontend files when include_frontend is false
[conditional.'!include_frontend']
ignore = ["src/frontend/", "package.json"]Run Rhai scripts after generation:
[hooks]
post = ["post-generate.rhai"]Example post-generate.rhai script:
// Rename a directory based on user selection
let 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.
Organize multiple templates in one repository:
icp-templates/
├── motoko-basic/
│ └── cargo-generate.toml
├── rust-basic/
│ └── cargo-generate.toml
└── full-stack/
└── cargo-generate.toml
Use with --subfolder:
icp new my-project --git https://github.com/org/icp-templates --subfolder motoko-basicThe 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/.
Test without publishing:
# Test from local directory
icp new test-project --path ./my-template
# Verify the generated project
cd test-project
icp network start -d
icp deployBefore 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
- Push your template to GitHub
- Users can reference it directly:
icp new my-project --git https://github.com/username/my-templatePin to specific versions:
# Use a tag
icp new my-project --git https://github.com/user/template --tag v1.0.0
# Use a branch
icp new my-project --git https://github.com/user/template --branch stableThe 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
- Tutorial — Use templates to create projects
- Creating Recipes — Create reusable build configurations