Django templates, edited where they render
A pip install and one line in INSTALLED_APPS, and every element carries its own file, line and column — through extends, include and block. Your TEMPLATES setting is not touched. Pro, early access, opt-in.
How do I edit Django templates visually?
Editing a Django template visually needs the rendered page to remember where it came from. By the time the browser has the HTML, the extends chain, the included partials and the block overrides have collapsed into one document with nothing left to say which file wrote which line. The block editors in this space avoid the problem rather than solving it, by storing content as data instead of touching templates. Recovering the mapping means hooking the one point every Django template passes through and stamping each element with its file, line and column as the template is constructed. PortBay ships that as a PyPI package, portbay-django-stamper, and edits are byte-spliced back into the template file. Measured on served HTML against a zero-stamp control: 18 of 18 coordinates exact on Django 6.1 and 5.2.17.
Verified against the product, 2026-09-05.
A Django project, running and clickable
PortBay runs your Django app at its own .test URL with trusted HTTPS and a per-project PostgreSQL or MySQL database, and dispatches board cards into it. The visual editor edits that same running app.
From click to template file
Four steps. Step three has two parts, and skipping the second is the one mistake that leaves you with a package installed and nothing stamped.
- Install PortBay and add your Django project
- Turn the visual editor on
- Install the package, then register the app
- Open Edit in browser and change something
Install PortBay and add your Django project
Homebrew installs the app and the bundled portbay CLI. Add the project folder and PortBay detects Django from manage.py at the project root, provisions a database, and serves the app at its own .test hostname over trusted HTTPS.
brew tap portbay-app/portbay && brew install --cask portbayTurn the visual editor on
It ships to Pro under early access and is off by default. Sign in to Pro, then flip the Early Access toggle. The open-source build does not include the editor at all — it is not compiled in.
Settings › Account › Early AccessInstall the package, then register the app
Both halves are required. Django has no package auto-discovery — no equivalent of Laravel's provider discovery or the Rails Railtie — so nothing is stamped until the app is named in INSTALLED_APPS. Watch the spelling: you install portbay-django-stamper with hyphens, and you register portbay_loc with an underscore. It needs Python 3.9 or newer and Django 4.2 or newer.
pip install portbay-django-stamper → INSTALLED_APPS += ["portbay_loc"]Open Edit in browser and change something
The project's Visual editing section has one button. It opens your normal browser at your normal .test URL with the edit bar docked at the top, booting in Preview mode where the page stays fully interactive. Switch to Edit, double-click text to type into the live page, then Save to write to source.
https://myapp.testWhy it never touches your TEMPLATES setting
The obvious way to instrument Django templates is to ship a loader. That works, and it costs you a settings block you then own forever — which is why this package does something else.
Instead the package patches the single constructor every Django template passes through — so filesystem, app_directories, cached and locmem loaders are all covered at once, and your APP_DIRS and resolution order stay exactly as you left them.
# settings.py — the whole installINSTALLED_APPS = [ # ... "portbay_loc", # underscore; the pip name is hyphenated] # What a loader-based approach WOULD have made you write,# and then maintain, and then get subtly wrong:TEMPLATES = [{ "BACKEND": "django.template.backends.django.DjangoTemplates", "APP_DIRS": True, # <- would have to be deleted "OPTIONS": { "loaders": [ # <- spelled out by hand, order matters "django.template.loaders.filesystem.Loader", "django.template.loaders.app_directories.Loader", # ... plus the stamping loader, wrapped correctly ], },}]
The pieces underneath
Visual editor
Click an element on your running site in your own browser and edit its text, classes and styles straight into the source file, across 21 stacks.
Explore →Agent dev environment
Stack detection, runtimes, HTTPS, databases and mail, provisioned per project — the running app your agents verify their work against.
Explore →Asked before downloading
One entry in INSTALLED_APPS: "portbay_loc". That is the whole configuration. Note the two names differ — the distribution you pip install is portbay-django-stamper with hyphens, and the importable app you register is portbay_loc with an underscore. Installing without registering is the one failure mode that looks like nothing happening, because Django has no package auto-discovery to fall back on.
No, deliberately. Shipping a template loader would have forced you to spell out OPTIONS['loaders'] by hand, which means deleting APP_DIRS and writing out the stock loaders yourself — a settings block you would then maintain forever, and one that silently changes template resolution order if you get it wrong. Instead the package patches the single Template constructor that every loader ends up calling, so your loaders and APP_DIRS stay exactly as they are.
Yes, and each element points at the file it was actually written in rather than the file it rendered through. A block defined in a child template and rendered into a base points at the child; an included partial points at the partial. On served HTML measured against a zero-stamp control, 18 of 18 coordinates were exact on both Django 6.1 and 5.2.17, covering an extends base, a child block, an included partial and a for loop over an include.
Yes. An htmx fragment is markup your server rendered through the same stamped templates, so the coordinates travel with the response — an htmx-style fragment measured 2 of 2 exact in the same run. Fragments assembled on the client, which the server never saw, are a different matter and fall back to text search.
Templates that are not .html or .htm, templates with no file behind them, and templates that live outside your project — a third-party app's templates get no coordinate. The reasoning is that a coordinate pointing into a file you cannot edit is worse than no coordinate at all, so those elements fall back to the text-search tier instead of claiming a precision they do not have.
No. The stamp is applied to the template source as a string in memory, on its way into Django's Template constructor — the file on disk is never opened for writing, so no data-pb-loc attribute ever reaches your repository. Stamping is on when DEBUG is on, and PORTBAY_LOC=0 or 1 overrides it either way; the shell form works, because manage.py runserver passes its whole environment through to the child process.
No. It is a Pro feature, still in early access behind an opt-in toggle in Settings, and it is not compiled into the open-source build at all. It is also unavailable on a project while that project is publicly tunneled — the edit bar and its write-back endpoints are never routed onto public traffic. Running Django, the database, the .test hostname and trusted HTTPS are all in the free, open-source app.

Give your projects and your agents a real local home.
Download for macOSFree & open source · macOS 11+ on Apple Silicon · Pro from $10/mo