Sub-templates#
A Cookieplone template repository can expose multiple independent templates through a single root cookiecutter.json.
These are called sub-templates.
Why sub-templates exist#
A large project (for example, a full Plone site) may consist of several composable parts:
A backend package.
A frontend add-on.
A Docker Compose configuration.
A CI/CD pipeline.
Rather than bundling everything into one monolithic template, you can define each part as a separate sub-template. A post-generation hook in the main template then calls the sub-templates programmatically to assemble the final project.
This keeps each sub-template focused, testable, and reusable independently.
Declaring sub-templates#
There are two levels where sub-templates appear in a Cookieplone repository.
Repository-level templates key#
The root cookiecutter.json lists all templates the repository provides under the templates key:
{
"templates": {
"project": {
"path": "./templates/project",
"title": "A Plone project",
"description": "Full Plone project with backend and frontend."
},
"backend": {
"path": "./templates/backend",
"title": "Backend package",
"description": "Plone backend package only.",
"hidden": true
},
"frontend": {
"path": "./templates/frontend",
"title": "Volto frontend add-on",
"description": "Volto frontend add-on only.",
"hidden": true
}
}
}
In this example:
projectis the main template, visible to users.backendandfrontendare sub-templates called programmatically; they are hidden from the menu.
Template-level: config.subtemplates#
Inside each template's cookieplone.json (v2 format), the config.subtemplates array declares which sub-templates should run after generation.
Each entry has an id, a title, and an enabled field:
{
"config": {
"subtemplates": [
{"id": "sub/backend", "title": "Backend", "enabled": "1"},
{"id": "sub/frontend", "title": "Frontend", "enabled": "{{ cookiecutter.has_frontend }}"}
]
}
}
The enabled field can be a static value ("1" or "0") or a Jinja2 expression.
Expressions are rendered against the current template context after the user completes the wizard, allowing sub-templates to be conditionally enabled based on user answers.
During generation, these entries are converted into [id, title, enabled] lists and injected into the template context as __cookieplone_subtemplates, where post-generation hooks can read them.
See Schema v2 reference (cookieplone.json) for the full specification of the config.subtemplates format.
Calling sub-templates from a hook#
A post-generation hook in the main template triggers the sub-templates after the top-level project is rendered.
Cookieplone ships a helper, cookieplone.utils.subtemplates.run_subtemplates(), that reads the __cookieplone_subtemplates entries from the context and dispatches each one:
# hooks/post_gen_project.py
from collections import OrderedDict
from pathlib import Path
from cookieplone.utils.subtemplates import run_subtemplates
context: OrderedDict = {{cookiecutter}}
def main():
output_dir = Path.cwd()
# {{ cookiecutter.__cookieplone_subtemplates }}
run_subtemplates(context, output_dir)
if __name__ == "__main__":
main()
For each enabled entry, run_subtemplates():
Skips it (with a log line) when
enabledevaluates to0.Calls a custom handler if you registered one for that sub-template
id.Otherwise falls back to a default call to
cookieplone.generator.generate_subtemplate()using the entry'sfolder_name.
Custom handlers#
Most real projects need per-sub-template tweaks: injecting extra context keys, rewriting the output folder, or post-processing generated files.
Pass a handlers dict mapping template_id → callable with signature (context, output_dir) -> Path:
from cookieplone.utils.subtemplates import run_subtemplates
def generate_backend(context, output_dir):
context["feature_headless"] = "1"
return generator.generate_subtemplate(
"templates/add-ons/backend", output_dir, "backend", context,
)
SUBTEMPLATE_HANDLERS = {
"add-ons/backend": generate_backend,
}
run_subtemplates(context, output_dir, handlers=SUBTEMPLATE_HANDLERS)
Handlers receive a deep copy of the context, so in-place mutations are safe and do not leak across sub-templates.
For a full real-world example that registers seven handlers covering backend, frontend, docs, CI, IDE, and shared sub-templates, see Call sub-templates from a hook.
Independent sub-templates#
Sub-templates do not have to be hidden.
A repository can expose several equally prominent templates—for example, a project template and a standalone addon template—each runnable on its own.