Installation

Set up Tailwind, Alpine and django-cotton, then start adding components.

Requirements

  • Python 3.12 or newer
  • Django 4.2 or newer
  • django-cotton 2.7.1 or newer, which provides the <c-...> template syntax every component is written in
  • Tailwind CSS 4.1 or newer. Several components use utilities that landed in 4.1, and a build on 4.0 drops them without an error — the component renders, missing a rule here and there
  • Alpine.js, for the components that are interactive

Which django-cotton

Your Django version decides this for you, so it is worth knowing before you pick one:

django-cotton Works with
1.x Django 4.2 to 5.1
2.x Django 4.2 to 6.x

On Django 5.2 or newer, 2.x is the only line that installs. And versions below 2.7.1 carry a fix worth having: a dynamic attribute holding a quote could break out of the attribute and inject arbitrary ones. Pin the floor:

django-cotton>=2.7.1

The components themselves run on both lines — every one of them is rendered against 1.6 and against 2.7 on each change — so an older project keeps working. The floor is about the injection fix, not about the markup.

Set up the project

Install django-cotton and add it to your settings:

INSTALLED_APPS = [
    ...
    "django_cotton",
]

Then initialize the theme. This creates templates/cotton/ and drops an input.css holding the palette, the design tokens and the Geist font faces that every component builds on:

uvx django_shadcn@latest init

Running it again keeps the input.css you have and says so — your palette is never silently replaced. Pass --force when you do want the shipped one back.

Point Tailwind at that file and let it watch your templates:

npx @tailwindcss/cli -i input.css -o static/css/output.css --watch

Finally, load the stylesheet and Alpine from your base template:

<link rel="stylesheet" href="{% static 'css/output.css' %}" />
<script src="{% static 'js/alpine.min.js' %}" defer></script>

Serving Alpine from your own static files keeps the page working offline and avoids trusting a CDN at runtime. If you would rather load it from one, pin the version instead of tracking latest.

Alpine plugins

Two components — accordion and collapsible — animate through @alpinejs/collapse, which Alpine's core does not include. Without it they render correctly and simply never animate: no error, nothing in the console.

Load the plugin before Alpine itself:

<script src="{% static 'js/collapse.min.js' %}" defer></script>
<script src="{% static 'js/alpine.min.js' %}" defer></script>

add names any plugin a component needs at the end of the run, so you do not have to remember which ones do.

Add a component

uvx django_shadcn@latest add button

Components land in templates/cotton/<name>/, and their dependencies come along automatically. You can name several at once:

uvx django_shadcn@latest add button card input

Use it like any other cotton component:

<c-button variant="outline">Click me</c-button>

Your files stay yours

add never overwrites what is already on disk. Running it a second time reports the files it skipped and leaves your edits alone — the components are yours to change once they land.

Two flags change that, on purpose:

Flag Existing file Removes anything
(default) skipped never
--overwrite replaced never
--sync replaced yes, mirrors the source

--sync lists what it is about to delete and asks before doing it. Pass --yes to skip the prompt in a script.

Add --dry-run to any of them to see the outcome without it happening:

uvx django_shadcn@latest add button --sync --dry-run

It prints the same per-file report the real run would, and writes nothing. This is worth the habit before --sync, the one mode that deletes.

Find what is available

uvx django_shadcn@latest list

Installed components are marked, so you can tell at a glance what a project is already using.