Skip to content

Project structure

Where do files go? Honestly: wherever you'd put them in any other Django project. reflex-django doesn't impose a layout on you. Below is the layout we recommend because it's the one most teams find easiest to work with.


myproject/
├── manage.py
├── pyproject.toml
├── db.sqlite3
├── config/                       # Django project package
│   ├── settings.py               # INSTALLED_APPS, MIDDLEWARE, REFLEX_DJANGO_*
│   ├── urls.py                   # reflex_mount(...) — last line
│   ├── asgi.py                   # imports reflex_django.asgi_entry:application
│   └── wsgi.py
├── shop/                         # a Django app
│   ├── models.py                 # your ORM models
│   ├── admin.py                  # admin registrations
│   ├── views.py                  # Reflex pages + state live HERE
│   ├── serializers.py            # optional ReflexDjangoModelSerializer
│   └── migrations/
├── blog/                         # another app — blog/views.py is auto-discovered
│   ├── models.py
│   └── views.py
└── .web/                         # generated by Reflex on build (gitignore)

Three things to notice:

  1. One manage.py, at the repo root. Same as any Django project.
  2. Pages and ORM live in the same app folder. shop/models.py and shop/views.py are next to each other. Your Reflex pages and your Django models naturally share that context.
  3. No rxconfig.py. No shop/shop.py. Configuration lives in urls.py. The Reflex app instance is loaded from reflex_django.django_led_app.

What goes where

Location What lives there
config/settings.py Django apps, middleware, database, REFLEX_DJANGO_* overrides
config/urls.py Your Django routes, then reflex_mount(...) at the very bottom
config/asgi.py One import line pointing at reflex_django.asgi_entry:application
{app}/models.py Django ORM models (unchanged from any normal Django project)
{app}/views.py @template-decorated Reflex pages and AppState subclasses
{app}/serializers.py Optional ReflexDjangoModelSerializer classes
{app}/admin.py Django admin registrations (unchanged)
{app}/migrations/ Django migrations (unchanged)
Not present {app}/{app}.py — Reflex's app instance is auto-loaded
Not present rxconfig.py — config lives in urls.py

Generated and ignored paths

These directories are created by Reflex or manage.py run_reflex. You don't edit them, and you should .gitignore them:

.web/
.reflex/
staticfiles/
Path What's in it
.web/ Reflex's frontend project (the compiled React source)
.web/build/client/ The compiled SPA bundle (SSR layout)
.web/_static/ The compiled SPA bundle (legacy layout)
.reflex/ Reflex's per-user cache (may be global on your machine)
STATIC_ROOT/_reflex/ Final staging location served by ReflexMountView in production

How pages are discovered

When the server boots, reflex_django.django_led_app walks every entry in INSTALLED_APPS and tries to import {app}.views. Any @template / @page decorators in those modules register their routes. There's nothing for you to wire up.

The skip list: django.* apps and reflex_django itself are never scanned.

INSTALLED_APPS = [
    "django.contrib.admin",      # skipped
    "django.contrib.auth",       # skipped
    ...
    "reflex_django",             # skipped
    "shop",                      # shop/views.py auto-imported
    "blog",                      # blog/views.py auto-imported
]

If {app}/views.py doesn't exist, that app is silently skipped. If it raises an import error, you'll see it.


The app_name argument

reflex_mount(app_name="shop") names the primary app. It's used for a few things:

  • It's the Reflex app_name, which shows up in compiled artifacts and metadata.
  • If INSTALLED_APPS discovery is off (REFLEX_DJANGO_AUTO_DISCOVER_PAGES = False), only {app_name}.views is imported.
  • It doubles as the default app label for things like the auth pages.

If you don't pass it, reflex-django uses the folder name containing manage.py (with hyphens turned into underscores). For most projects, this default is fine and you can leave app_name off.


Alternative layout: pages in a separate package

Some teams prefer to keep the UI code in its own package, away from the domain code:

myproject/
├── shop/                      # models, admin, business logic
│   ├── models.py
│   └── admin.py
└── frontend/                  # all Reflex pages
    ├── pages/
    │   ├── home.py
    │   ├── catalog.py
    │   └── account.py
    └── views.py               # re-exports each page

Two ways to make this work:

Option A — put frontend in INSTALLED_APPS. frontend/views.py will be auto-discovered:

INSTALLED_APPS = [
    "shop",
    "frontend",   # frontend/views.py is auto-imported
]

Option B — list the modules explicitly in settings.py:

REFLEX_DJANGO_PAGE_PACKAGES = [
    "frontend.pages.home",
    "frontend.pages.catalog",
    "frontend.pages.account",
]

This disables auto-discovery and uses only what you list.

Both work. Pick the one your team prefers.


A reasonable .gitignore

# Python
__pycache__/
*.pyc
.venv/

# Django
db.sqlite3
staticfiles/

# Reflex
.web/
.reflex/

# Optional materialized rxconfig (only present if you opt in)
rxconfig.py

Static files

Environment How it works
Development manage.py run_reflex builds the SPA into STATIC_ROOT/_reflex/ automatically. Django's ASGIStaticFilesHandler serves both your admin assets and the SPA.
Production Run python manage.py export_reflex --frontend-only --no-zip --stage-to-static-root in CI, then python manage.py collectstatic, then serve /static/ with Nginx (or your ASGI process).

The Deployment guide has the full production setup.


Where to go next


Next: Configuration with reflex_mount() →