Home Features Docs Blog Security Examples FAQ
Documentation

Learn djust Inside & Out.

Everything you need to build reactive, real-time Django applications with djust.

State Management

Learn how to manage state in djust LiveViews for reactive updates.

State Basics

In djust, state lives on the server. Any instance attribute on your LiveView becomes state that's tracked and triggers re-renders when changed.

Simple State

class CounterView(LiveView):
    def mount(self, request, **kwargs):
        self.count = 0  # This is state
        self.message = "Hello"  # This is also state

    def increment(self):
        self.count += 1  # Triggers re-render

Declarative State

Use the @state decorator for cleaner syntax:

from djust import LiveView, state, event_handler

class ProfileView(LiveView):
    # State with default values
    first_name = state(default='')
    last_name = state(default='')
    email = state(default='')

    @event_handler
    def update_name(self, first: str = '', last: str = '', **kwargs):
        self.first_name = first
        self.last_name = last

Computed Properties

Derive values from state:

from djust import LiveView, state, computed

class CartView(LiveView):
    items = state(default=[])
    tax_rate = state(default=0.08)

    @computed
    def subtotal(self):
        return sum(item['price'] * item['qty'] for item in self.items)

    @computed
    def tax(self):
        return self.subtotal * self.tax_rate

    @computed
    def total(self):
        return self.subtotal + self.tax

Streams

Streams are memory-efficient collections for large datasets. Items are cleared from server memory after each render, but the client preserves DOM elements.

Basic Usage

class FeedView(LiveView):
    def mount(self, request, **kwargs):
        # Initialize stream with items
        self.stream('posts', Post.objects.all()[:100])

    def add_post(self, content):
        post = Post.objects.create(content=content)
        self.stream_insert('posts', post)  # Append

    def remove_post(self, post_id: int):
        Post.objects.filter(id=post_id).delete()
        self.stream_delete('posts', post_id)

Template

<ul dj-stream="posts">
    {% for post in streams.posts %}
        <li id="posts-{{ post.id }}">{{ post.content }}</li>
    {% endfor %}
</ul>

Stream Operations

Method Description
stream(name, items) Initialize or update stream
stream_insert(name, item, at=-1) Insert item (-1=end, 0=start)
stream_delete(name, item_or_id) Remove item by ID or object

Temporary Assigns

For large collections without streams, use temporary_assigns to clear data from server memory after each render.

class ChatView(LiveView):
    # Clear messages from memory after each render
    temporary_assigns = {'messages': []}

    def mount(self, request, **kwargs):
        self.messages = Message.objects.all()[:50]

    def new_message(self, content):
        msg = Message.objects.create(content=content)
        self.messages = [msg]  # Only new message sent

Template with Append

<ul dj-update="append" id="messages">
  {% for msg in messages %}
    <li id="msg-{{ msg.id }}">{{ msg.content }}</li>
  {% endfor %}
</ul>

Best Practices

Keep State Minimal

# Good
self.selected_ids = {1, 2, 3}

# Bad
self.selected_items = [obj1, obj2, obj3]

Use Streams for Large Collections

# Good
self.stream('items', Item.objects.all()[:100])

# Bad
self.items = list(Item.objects.all()[:100])

Batch State Updates

# Good
self.items = items  # Single update

# Bad
for item in items: self.items.append(item)  # Multiple re-renders!

Next Steps