Cookiecutter Cheatsheet

July 6, 2026 · View on GitHub

Installation

pip install cookiecutter
pipx install cookiecutter   # recommended

CLI

# From URL
cookiecutter https://github.com/user/cookiecutter-template.git

# From local path
cookiecutter ./my-template

# Non-interactive
cookiecutter --no-input ./my-template project_name=foo

# Specify branch/tag
cookiecutter https://github.com/user/template.git --checkout v1.0

# Output directory
cookiecutter ./my-template --output-dir /tmp/projects

# Overwrite if exists
cookiecutter ./my-template --overwrite-if-exists

# Skip if exists
cookiecutter ./my-template --skip-if-file-exists

# Subdirectory within repo
cookiecutter https://github.com/user/repo.git --directory templates/python

# Keep project if hook fails
cookiecutter ./my-template --keep-project-on-failure

cookiecutter.json

{
  "project_name": "My Project",
  "project_slug": "{{ cookiecutter.project_name|lower|replace(' ', '_') }}",
  "version": "0.1.0",
  "license": ["MIT", "Apache-2.0", "proprietary"],
  "use_docker": ["no", "yes"]
}
TypeBehavior
StringFree text prompt, default value
ArraySelection prompt (choices)
Jinja2 {{ }}Derived variable, not prompted

Jinja2 in Templates

{{ cookiecutter.project_name }}                              # variable
{{ cookiecutter.project_name|lower }}                        # filter
{{ cookiecutter.project_name|replace(' ', '_') }}            # replace
{{ cookiecutter.project_name|lower|replace(' ', '_') }}      # chained
{{ cookiecutter.use_docker|tojson }}                         # to JSON

{% if cookiecutter.use_docker == "yes" %}...{% endif %}      # conditional
{% if %}...{% elif %}...{% else %}...{% endif %}             # if/elif/else
{% for item in cookiecutter.items.split(',') %}...{% endfor %}  # loop
{%- if %}...{%- endif -%}                                    # whitespace control
{{ "{{ not_jinja }}" }}                                      # literal escape

Hooks

hooks/
├── pre_gen_project.py     # before generating (validate)
└── post_gen_project.py    # after generating (clean, git init)
# pre_gen_project.py
import sys
if '{{ cookiecutter.project_name }}' == '':
    print('ERROR: project_name is empty')
    sys.exit(1)
# post_gen_project.py
import os
if '{{ cookiecutter.use_docker }}' == 'no':
    os.remove('Dockerfile')

Special Files

_copy_without_render     # list of paths without Jinja2
_extensions/             # custom Jinja2 filters

User Config

# ~/.cookiecutterrc
default_context:
  author_name: "Jane Doe"
  license: "MIT"

Useful Filters

FilterExampleResult
lower"My Project"|lowermy project
upper"my"|upperMY
title"my project"|titleMy Project
replace"My Project"|replace(' ', '_')My_Project
trim" foo "|trimfoo
length"abc"|length3
tojson"yes"|tojson"yes"

Naming Conventions

VariableConventionExample
project_nameHuman readableMy Awesome Project
project_slugsnake_casemy_awesome_project
pkg_nameno separatorsmyawesomeproject
repo_namekebab-casemy-awesome-project
class_namePascalCaseMyAwesomeProject