Template params
Param types
[params]
project_name = { type = "text", prompt = "Project name", default = "myproject" }
description = { type = "textarea", prompt = "Description" }
port = { type = "number", prompt = "Port", default = 8080, min = 1024, max = 65535 }
license = { type = "license", prompt = "License", default = "MIT" }
features = { type = "multiselect", prompt = "Features", options = ["auth", "db", "cache"] }
private = { type = "bool", prompt = "Private repo?", default = false }
output_dir = { type = "path", prompt = "Output directory" }
api_key = { type = "secret", prompt = "API key" }

License params
The license param type is special: it offers a curated list of SPDX license identifiers plus a "None" option, and when a license is selected spin generates a LICENSE file in the user's project at render time, with the copyright year and holder substituted automatically.
[params]
license = { type = "license", prompt = "License", default = "MIT" }
copyright_holder = { type = "text", prompt = "Copyright holder", default = "Acme Corp" }
license— options are auto-filled from the supported licenses set. You can restrict the choice by providing your ownoptions; any value outside the set yields noLICENSEfile.copyright_holder— a plaintextparam that populates the copyright line in the generated license. It is optional;defaultand templated defaults work like any text param. When absent, the ownership placeholder is left as-is in the license text.
See License generation for full details.
Shorthand form
A param with only a default value can be written as a plain string:
[params]
project_name = "myproject" # equivalent to { type = "text", default = "myproject" }
Using params in templates
Inside a .tmpl file, params are top-level fields:
# {{ .project_name | title }}
{{ .description }}
- License: {{ .license }}
- Port: {{ .port }}
- Features: {{ join .features ", " }}
Non-interactive usage
Users pass params as --param key=value:
spin new myapp --template my-template \
--param project_name=myapp \
--param port=8080 \
--param features=auth,db
Multiselect values are comma-separated. Booleans accept true, false, 1, 0, yes, no, on, off.
Templated prompts and defaults
prompt and default accept Go templates, so they can reference other
values with the same spin helpers used in .tmpl files and hooks
(upper, snake_case, {{ .project_name }}, ...):
[params]
project_name = { type = "text", prompt = "Project name" }
module = { type = "text", default = "github.com/me/{{ .project_name }}" }
db_name = { type = "text", prompt = "Database name for {{ .project_name }}" }
Only values known before the form runs are available when a prompt or
default is rendered: the built-ins name and project_name (injected
from the project name you pass to spin new) and anything supplied via
--param. A prompt or default that references another param's
answer will not reflect what the user types into the form, because that
answer does not exist yet when the form is built.
If a template string fails to parse or references an unknown value, spin
leaves it as-is — it never aborts a scaffold over one bad string. Double
check variable names: a typo like {{ .projet_name }} renders to an empty
string rather than erroring.
Validation
numberparams enforceminandmax.selectparams reject values not inoptions(when options are provided). This applies both interactively and to--paramvalues and templated defaults resolved outside the option list.
Template anatomy
A template is a directory with a spin.toml manifest and a base/ tree. spin loads it, collects params, renders the tree, runs pre-hooks, writes the files, run...
Template rendering
How spin processes _base/ files through Go text/template with built-in helpers, excludes, and path safety.