Brings full stack components to Django.
Project description
Interlace: Reactive Server-Side Components for Django
Project Overview
Interlace brings reactive, server-driven components to Django inspired by Laravel Livewire and Phoenix LiveView. Build interactive web applications using Python and Django templates—no JavaScript build tools required.
Key Features
Server-Driven Architecture: All state lives in Python, components render on the server, and only HTML changes are sent to the client. Write reactive UIs entirely in Python with full access to the Django ORM and ecosystem.
Efficient DOM Morphing: Uses morphdom to intelligently update only changed elements, preserving focus states, scroll positions, and CSS transitions. No full page reloads, no virtual DOM overhead.
Reactive Data Binding: Two-way binding with lace:model, instant updates with .live modifiers, debouncing with .debounce, and automatic form handling. Components re-render automatically when properties change.
Rich Component Features: Lifecycle hooks (mount, updated, rendered), nested components with parent-child communication, lazy loading with viewport detection, polling for real-time updates, and built-in validation.
Zero Build Step: Minimal client-side JavaScript (~30KB), no npm, no webpack, no build pipeline. Drop into existing Django projects with minimal configuration.
Installation & Setup
Prerequisites
- Python 3.10+
- Django 4.2+
Quick Installation
pip install django-interlace
Or with Poetry:
poetry add django-interlace
The distribution is
django-interlaceon PyPI; the import package isinterlace(from interlace import Component).
Django Configuration
Add to INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
# ...
'interlace',
]
Include Interlace URLs in urls.py:
from django.urls import path, include
urlpatterns = [
# ...
path('interlace/', include('interlace.urls')),
]
Add Interlace's JavaScript to your base template:
{% load static %}
<script src="{% static 'interlace/interlace.js' %}" defer></script>
Quick Start
Create a Component
# app_name/interlace/Counter.py
from interlace import Component
class Counter(Component):
count: int = 0
def increment(self):
self.count += 1
def decrement(self):
self.count -= 1
def inline_template(self):
return """
<div>
<h2>Count: {{ count }}</h2>
<button lace:click="increment">+</button>
<button lace:click="decrement">-</button>
</div>
"""
Use in Templates
{% load interlace %}
<div>
<h1>My Counter App</h1>
{% interlace "Counter" %}
</div>
That's it! The counter is now reactive—clicking buttons updates the count without page reloads.
Component Architecture
Dataclass-Style Components
Components use type hints and default values for clean property definitions:
class UserProfile(Component):
username: str = ""
email: str = ""
is_admin: bool = False
tags: list = []
def mount(self):
# Called when component initializes
user = User.objects.get(id=self.user_id)
self.username = user.username
self.email = user.email
Template Options
Inline templates:
def inline_template(self):
return """<div>{{ content }}</div>"""
External template files:
# Looks for app_name/interlace/templates/component_name.html
template_name = "user_profile.html"
Event Directives
lace:click="method"- Handle clickslace:model="property"- Two-way data bindinglace:model.live="property"- Instant updateslace:model.debounce.300ms="property"- Debounced updateslace:init="method"- Run on component mountlace:poll.2s="method"- Poll every 2 seconds
Lifecycle Hooks
def mount(self):
# Called once when component initializes
pass
def updated_property_name(self, value):
# Called when specific property changes
pass
def updated(self, property, value):
# Called when any property changes
pass
def rendered(self):
# Called after component renders
pass
Example Project Structure
my_app/
├── models.py # Django models
├── views.py # Django views
├── urls.py # URL routing
├── admin.py # Admin configuration
├── interlace/ # Interlace components
│ ├── __init__.py
│ ├── Counter.py # Counter component
│ ├── TodoList.py # Todo list component
│ ├── UserProfile.py # User profile component
│ └── ProductFilter.py # Product filter component
└── templates/
└── interlace/ # Component templates (optional)
├── counter.html
├── todo_list.html
└── user_profile.html
Advanced Features
Query Parameters
Sync component state to URL for shareable, bookmarkable pages:
class ProductFilter(Component):
search: str = ""
category: str = "all"
query_params = ['search', 'category']
Lazy Loading
Defer expensive operations until component is visible:
{% interlace "HeavyComponent" lazy=True %}
Nested Components
Components can render other components and communicate via events:
def delete_item(self):
self.emit_to('parent_list', 'item_deleted', {'id': self.item_id})
Computed Properties
Cache expensive calculations with @computed:
from interlace import Component, computed
class ReportBuilder(Component):
start_date: str = ""
end_date: str = ""
@computed
def filtered_events(self):
# Cached within request - multiple template accesses won't re-query
return Event.objects.filter(date__range=[self.start_date, self.end_date])
@computed(persist=True, key=['start_date', 'end_date'], seconds=300)
def performance_data(self):
# Cached across requests for 5 minutes, per component instance
return calculate_kpis(self.start_date, self.end_date)
@computed(cache=True, key=['start_date'], seconds=1800)
def daily_totals(self):
# Global cache shared across all users
return DailyStats.objects.filter(date=self.start_date).aggregate(...)
Database Integration
Direct ORM access in components:
def mount(self):
self.posts = Post.objects.filter(published=True).select_related('author')
def search(self, query):
self.posts = Post.objects.filter(title__icontains=query)
Documentation
- Full API reference:
interlace/AGENTS.md— a dense, example-first reference covering every directive, property type, lifecycle hook, and gotcha. It ships inside the package, so once installed it's also atsite-packages/interlace/AGENTS.md(handy for AI/agent sessions in a consuming project). - Website: [Coming soon]
- Examples: See
/dev/example_project
Development
Running Tests
# In Docker (recommended)
docker exec -t django-web-app python manage.py test --parallel
# Locally
cd dev/example_project
python manage.py test
Creating Components
python manage.py interlace create ComponentName
Similar Projects
- Laravel Livewire - PHP framework that inspired Interlace
- Phoenix LiveView - Elixir framework with similar architecture
- HTMX - Library for HTML-over-the-wire patterns
- djust - Django + Rust alternative with similar goals
License
MIT License - see LICENSE file for details
Contributing
Contributions welcome! Please see the /dev directory for the development setup.
Built with ❤️ for the Django community
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_interlace-0.2.1.tar.gz.
File metadata
- Download URL: django_interlace-0.2.1.tar.gz
- Upload date:
- Size: 160.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.11.0 Linux/6.17.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d7e65cc17769ec3f5a74ca56a0568bd50f00e0ca2a0d64860daf533dd96f3143
|
|
| MD5 |
d49fcdb9105ba1f560d639d5f74f7d35
|
|
| BLAKE2b-256 |
230de5abd88b41c0545e17a0ce384231dbff6e7fb52224bdd157bac48cb29b79
|
File details
Details for the file django_interlace-0.2.1-py3-none-any.whl.
File metadata
- Download URL: django_interlace-0.2.1-py3-none-any.whl
- Upload date:
- Size: 207.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.4.1 CPython/3.11.0 Linux/6.17.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
768f7d39abf2f7ac1526b1821af755cc47698309af40f4f9951fb4370cb66933
|
|
| MD5 |
f3a2972afefa18850fea9d9adc8b1a4e
|
|
| BLAKE2b-256 |
4b83e315298c54e7431dc033b0bd9da61ec769ee01d010a2cb936cd9bc9eebfd
|