Skip to content

Latest commit

 

History

History
295 lines (215 loc) · 6.89 KB

File metadata and controls

295 lines (215 loc) · 6.89 KB
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.toml configuration file

Quick Start

Minimal Template

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.mo

Filenames with handlebar placeholders like {{project-name}}.did will be renamed with value.

Using Your Template

# 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-template

Template 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:

# 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 %}

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
└── .gitignore

Configuration 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 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"]

Advanced Features

Conditional Content

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 %}

Conditional Files

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"]

Post-Generation Hooks

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.

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.toml

Use with --subfolder:

icp new my-project --git https://github.com/org/icp-templates --subfolder motoko-basic

Example 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 directory
icp new test-project --path ./my-template

# Verify the generated project
cd test-project
icp network start -d
icp deploy

Validation Checklist

Before publishing, verify:

  • icp new completes 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

  1. Push your template to GitHub
  2. Users can reference it directly:
icp new my-project --git https://github.com/username/my-template

With Tags/Branches

Pin 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 stable

Official Templates

The default templates are in github.com/dfinity/icp-cli-templates. To contribute:

  1. Fork the repository
  2. Add your template as a subfolder
  3. Submit a pull request

Next Steps

Browse all documentation →