CLI tool
cookiecutter/cookiecutter avatar
cookiecutter/cookiecutter

cookiecutter: project scaffolding from a template directory and a cookiecutter.json

A cross-platform command-line utility that creates projects from cookiecutters (project templates), e.g. Python package projects, C projects.

25,115 stars2,283 forksPythonBSD-3-Clause

At a glance

What is it?
Cookiecutter turns a directory of Jinja2 placeholders into a runnable project. This review covers the CLI workflow, the hook mechanism, and where the template model breaks down.
Who is it for?
Adopt cookiecutter if you maintain more than one repository that shares structure, or if you want contributors to start from a known-good skeleton without reading a setup wiki. Skip it if your scaffolding needs conditional logic that only a full generator framework can express, or if you cannot review arbitrary hook scripts before running a third-party template.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem cookiecutter solves: repeated directory setup across repositories

Every new repository starts with the same chores. Create the directory tree, write a pyproject.toml or Cargo.toml, add a README, wire up a test directory, pick a licence file. Doing this by hand is slow and produces drift: two services in the same organisation end up with different test layouts because two different people created them on two different afternoons.

Cookiecutter addresses that by treating a project skeleton as a reusable artifact. The README describes it as a "cross-platform command-line utility that creates projects from cookiecutters (project templates)". A cookiecutter is a directory tree that contains placeholder variables. Running the tool copies that tree, substitutes the variables, and writes the result into your working directory.

The audience is broader than Python developers, which is worth noting because the project is written in Python and distributed on PyPI. The README states that templates can be "in any language or markup format", and lists Python, Rust, Terraform, and docs sites among the things people generate. The pyproject.toml classifiers mark it Production/Stable and restrict it to Python 3.10 through 3.14 for the tool itself, not for what it generates.

The second audience is template authors. They write the skeleton once and publish it, and consumers answer prompts. The README frames this as "One file defines the interface": cookiecutter.json declares every variable and its default.

How a cookiecutter run works: prompts, Jinja2 substitution, and hooks

The mechanism is a three-stage pipeline. First, cookiecutter reads cookiecutter.json from the template root. That file is a JSON object mapping variable names to default values. Each key becomes a prompt, and the default is what the user gets by pressing enter.

Second, the tool walks the template directory and renders every file and directory name through Jinja2. A path like {{cookiecutter.project_slug}}/src/{{cookiecutter.package_name}}/__init__.py is resolved with the values collected in stage one. Jinja2 is a declared dependency in pyproject.toml, pinned as Jinja2>=2.7,<4.0.0, so the templating language available inside files is standard Jinja2, including conditionals and loops.

Third, hooks fire. The README describes "pre-prompt, pre- and post-generate hooks" and says they are "shell or Python" scripts that "handle git init, dependency installs, or anything else your boilerplate needs". A post-generate hook is ordinary executable code that runs after the files are written, with access to the generated directory. That is the part that makes cookiecutter more than a copy command, and it is also the part that deserves scrutiny, since a template fetched from GitHub can run a script on your machine.

The data flow is one-directional. Values flow from cookiecutter.json defaults, through user answers, into Jinja2 rendering, and out to disk. There is no state file and no record of which template produced which project, so re-running a template over an existing directory is not a supported update path.

Installing cookiecutter and generating your first project

The README gives uv as the installation route for the CLI. This installs cookiecutter as an isolated tool rather than as a library in your current environment.

bash
uv tool install cookiecutter

After that, the cookiecutter command is on your PATH. The README's quickstart uses a GitHub-hosted template, and notes that repositories on GitHub can use the gh prefix for brevity.

bash
uvx cookiecutter gh:audreyfeldroy/cookiecutter-pypackage

You will be prompted for each variable declared in the template's cookiecutter.json. The README says the run "will create your Python package in the current working directory, based on those values". Expect a new directory named after whatever you entered for the project slug, containing the rendered tree.

A local template works the same way, with a filesystem path instead of a gh: reference.

bash
uvx cookiecutter cookiecutter-pypackage/

If you want to call it from Python rather than the shell, the README says to add it to your project with uv add cookiecutter and then import the main function. The same function accepts either a local path or a gh: reference.

python
from cookiecutter.main import cookiecutter

# Create project from the cookiecutter-pypackage/ template
cookiecutter('cookiecutter-pypackage/')

# Create project from the cookiecutter-pypackage.git repo template
cookiecutter('gh:audreyfeldroy/cookiecutter-pypackage')

That programmatic entry point is what makes cookiecutter usable inside a larger generator or an internal developer portal, rather than only as an interactive prompt.

Where cookiecutter is the wrong tool

The template model assumes the output is a directory of files with values substituted in. When your scaffolding needs to make decisions based on the answers, you are pushing Jinja2 conditionals into file contents and directory names, and that gets unwieldy quickly. A template that generates either a Flask app or a CLI tool by branching on a variable ends up with both variants interleaved in every file.

The second limitation is regeneration. Cookiecutter creates projects; it does not track them. There is no manifest written into the generated project, and the README does not document any update or re-apply command. If the upstream template changes, existing projects do not learn about it. Teams that need to push template changes into already-generated repositories are outside what this tool does.

Third, hooks are arbitrary code. The README describes post-generate scripts that "handle git init, dependency installs, or anything else your boilerplate needs". Anything else is a wide surface. A template pulled from a GitHub search result and run without reading its hooks is code execution from an untrusted source. This is not a flaw in the design so much as a property of it, but it means cookiecutter is a poor fit for environments where users cannot inspect a template before running it.

Finally, the project is not a web service or a hosted generator. There is no server component, no API endpoint, and no database. If you need a browser-based "create new project" button, cookiecutter would be a library you call from something else, not the thing users touch.

Cookiecutter compared with Copier and Yeoman

Copier is the closest alternative and the difference is architectural rather than cosmetic. Copier is built around updating generated projects: it records the answers and the template version in the project, so a later run can re-apply an updated template and merge the changes. Cookiecutter does not do this. Its model is one-shot generation, and the README lists no update command. If your problem is "keep fifty services in sync with a changing skeleton", Copier's approach fits better. If your problem is "produce a correct skeleton once, at the start", cookiecutter's simpler model is enough and has fewer moving parts.

Yeoman takes a different route again. It is a JavaScript ecosystem with generators built as Node packages, and the generator is a program you write rather than a directory of templates. That gives you full control over conditional logic and interactive prompts, at the cost of writing and maintaining that program. Cookiecutter's cookiecutter.json is declarative and much smaller, which is why templates are cheap to publish. The trade-off is that anything beyond variable substitution has to be expressed as Jinja2 inside files or as a hook script.

There is also the plain option: a git repository marked as a template on the hosting provider, cloned and renamed. That has no prompts and no substitution, but it also has no dependency to install and no hook that runs code. Cookiecutter earns its place when the number of variables is large enough that find-and-replace after cloning becomes error-prone.

Maintenance, releases, and the licence you inherit

The repository is not archived. The last push was on 2026-04-01, roughly five and a half months before today, so the project has not been pushed to in over six months. The release history shows v2.7.1 on 2026-03-04, v2.7.0 on 2026-03-02, and 2.6.0 before that on 2024-02-21. That gap between 2.6.0 and 2.7.0 is about two years, which tells you releases arrive in bursts rather than on a cadence. Plan for that: pin the version you install and do not assume a fix lands quickly.

The version declared in pyproject.toml is 2.7.1. The dependency set is small and mostly stable libraries: Jinja2, click, pyyaml, requests, arrow, rich, binaryornot, python-slugify. Jinja2 is capped below 4.0.0 and click below 9.0.0, so a major release of either will require a cookiecutter release before it can be adopted.

Licensing: cookiecutter itself is BSD-3-Clause, and the LICENSE file sits at the repository root. That covers the tool. It does not automatically cover the templates you run, which are separate repositories with their own licences, and it does not cover the output cookiecutter writes, which is your code. If you build an internal template, the licence you attach to it is your decision. This is a description of what the files say, not legal advice; check the licence of any third-party template before you ship generated code.

Editorial conclusion

Adopt cookiecutter if you maintain more than one repository that shares structure, or if you want contributors to start from a known-good skeleton without reading a setup wiki. Skip it if your scaffolding needs conditional logic that only a full generator framework can express, or if you cannot review arbitrary hook scripts before running a third-party template. Before adopting, check that the template you intend to use declares the variables you need in its cookiecutter.json, and confirm which hooks it ships, because post-generate hooks execute on your machine.

Frequently asked questions

What is cookiecutter?

It is a cross-platform command-line utility that creates projects from project templates, called cookiecutters. A cookiecutter is a directory containing placeholder variables, and cookiecutter copies it, substitutes your answers, and writes the result to disk.

How do I use cookiecutter?

Install it with uv tool install cookiecutter, then run uvx cookiecutter gh:audreyfeldroy/cookiecutter-pypackage or point it at a local template directory. You will be prompted for the values declared in the template's cookiecutter.json, and the project is created in the current working directory.

How do I install cookiecutter?

The README gives uv tool install cookiecutter as the CLI installation method. For programmatic use it says to run uv add cookiecutter in your project and import cookiecutter from cookiecutter.main.

How do I use cookiecutter with Python?

The README's quickstart uses a Python package template, gh:audreyfeldroy/cookiecutter-pypackage, and the generated project is a Python package. You can also call it from Python by importing cookiecutter from cookiecutter.main and passing a local path or a gh: reference.

How do I use cookiecutter with Django?

The README lists cookiecutter-django as one of the special templates, hosted at github.com/cookiecutter/cookiecutter-django. You would run it the same way as any GitHub-hosted template, passing that repository as the template argument.

Official sources

  1. cookiecutter/cookiecutter on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cookiecutter-cookiecutter.svg)](https://hysenlabs.com/projects/cookiecutter-cookiecutter)
Community notes

Community notes