Home Features Docs Blog Security Examples FAQ
Live Demos

Examples & Demos.

Explore real djust applications. Each demo is fully interactive with complete source code.

100% Server-Side Execution

Every click, input, and interaction you see below runs Python code on the server. No client-side JavaScript logic required. It just feels instant thanks to Rust-powered sub-millisecond VDOM diffing.

Real-Time Counter

Server-Side

Basic reactivity with server-side state management

0

Each click executes increment() Python method on server

views.py
 1from djust import LiveView, event_handler
 2
 3class CounterView(LiveView):
 4    template_name = 'counter.html'
 5
 6    def mount(self, request):
 7        """Initialize counter state"""
 8        self.count = 0
 9
10    @event_handler
11    def increment(self):
12        """Increment counter by 1"""
13        self.count += 1
14
15    @event_handler
16    def decrement(self):
17        """Decrement counter by 1"""
18        self.count -= 1
19
20    @event_handler
21    def reset(self):
22        """Reset counter to 0"""
23        self.count = 0
24
25    def get_context_data(self, **kwargs):
26        return {'count': self.count}
template.html
 1<div class="counter-app">
 2    <h2>Counter Example</h2>
 3
 4    <div class="count-display">
 5        {{ count }}
 6    </div>
 7
 8    <div class="button-group">
 9        <button dj-click="decrement" class="btn-danger">
10            Decrement
11        </button>
12        <button dj-click="reset" class="btn-secondary">
13            Reset
14        </button>
15        <button dj-click="increment" class="btn-success">
16            Increment
17        </button>
18    </div>
19</div>

Todo List (CRUD)

Server-Side

Full create, read, update, delete operations

No todos yet — add one above!
0 completed 0 total

Add, toggle, delete → all Python methods on server

views.py
 1from djust import LiveView, event_handler
 2
 3class TodoListView(LiveView):
 4    template_name = 'todo.html'
 5
 6    def mount(self, request):
 7        """Initialize todo list"""
 8        self.todos = []
 9        self.next_id = 1
10
11    @event_handler
12    def add_todo(self, text: str = "", **kwargs):
13        """Add new todo item"""
14        if text.strip():
15            self.todos.append({
16                'id': self.next_id,
17                'text': text,
18                'completed': False
19            })
20            self.next_id += 1
21
22    @event_handler
23    def toggle_todo(self, id: int = None, **kwargs):
24        """Toggle todo completion status"""
25        todo = next((t for t in self.todos if t['id'] == id), None)
26        if todo:
27            todo['completed'] = not todo['completed']
28
29    @event_handler
30    def delete_todo(self, id: int = None, **kwargs):
31        """Delete todo item"""
32        self.todos = [t for t in self.todos if t['id'] != id]
33
34    def get_context_data(self, **kwargs):
35        completed = sum(1 for t in self.todos if t['completed'])
36        return {
37            'todos': self.todos,
38            'completed_count': completed,
39            'total_count': len(self.todos)
40        }
template.html
 1<div class="todo-app">
 2    <h2>Todo List</h2>
 3
 4    <form dj-submit="add_todo" class="add-form">
 5        <input type="text"
 6               name="text"
 7               placeholder="What needs to be done?" />
 8        <button type="submit">Add</button>
 9    </form>
10
11    <div class="todo-list">
12        {% for todo in todos %}
13        <div class="todo-item">
14            <input type="checkbox"
15                   dj-change="toggle_todo"
16                   data-id="{{ todo.id }}"
17                   {% if todo.completed %}checked{% endif %} />
18            <span class="{% if todo.completed %}completed{% endif %}">
19                {{ todo.text }}
20            </span>
21            <button dj-click="delete_todo"
22                    data-id="{{ todo.id }}"
23                    class="delete-btn">
24                Delete
25            </button>
26        </div>
27        {% endfor %}
28    </div>
29
30    <div class="stats">
31        <span>{{ completed_count }} completed</span>
32        <span>{{ total_count }} total</span>
33    </div>
34</div>

Real-Time Form Validation

Server-Side

Instant feedback with Django Forms integration

Real-time validation with instant feedback

Validation runs validate_field() on every keystroke

views.py
 1from djust import LiveView, event_handler
 2from djust.forms import FormMixin
 3from .forms import SignupForm
 4
 5class SignupView(FormMixin, LiveView):
 6    form_class = SignupForm
 7    template_name = 'signup.html'
 8
 9    def mount(self, request):
10        """Initialize form view"""
11        self.success_message = None
12
13    @event_handler
14    def validate_field(self, field: str = None, value: str = "", **kwargs):
15        """Real-time field validation"""
16        # Validation happens automatically via FormMixin
17        # Errors are displayed in real-time
18        pass
19
20    def form_valid(self, form):
21        """Handle valid form submission"""
22        # Save the user
23        user = form.save()
24        self.success_message = "Account created successfully!"
25        # Reset form
26        self.form = self.form_class()
27
28    def form_invalid(self, form):
29        """Handle invalid form submission"""
30        # Errors displayed automatically
31        pass
forms.py
 1from django import forms
 2from django.contrib.auth.models import User
 3
 4class SignupForm(forms.ModelForm):
 5    """User signup form with validation"""
 6
 7    password = forms.CharField(
 8        widget=forms.PasswordInput,
 9        min_length=8,
10        help_text="At least 8 characters"
11    )
12
13    class Meta:
14        model = User
15        fields = ['email', 'password']
16
17    def clean_email(self):
18        """Validate email is unique"""
19        email = self.cleaned_data.get('email')
20        if User.objects.filter(email=email).exists():
21            raise forms.ValidationError(
22                "Email already registered"
23            )
24        return email
25
26    def clean_password(self):
27        """Validate password strength"""
28        password = self.cleaned_data.get('password')
29        if len(password) < 8:
30            raise forms.ValidationError(
31                "Password must be at least 8 characters"
32            )
33        return password
template.html
 1<div class="signup-form">
 2    <h2>Sign Up</h2>
 3
 4    <form dj-submit="submit_form">
 5        <div class="form-field">
 6            <label for="email">Email</label>
 7            <input type="email"
 8                   name="email"
 9                   dj-change="validate_field"
10                   value="{{ form.email.value|default:'' }}" />
11            {% if form.email.errors %}
12            <p class="error">{{ form.email.errors.0 }}</p>
13            {% endif %}
14        </div>
15
16        <div class="form-field">
17            <label for="password">Password</label>
18            <input type="password"
19                   name="password"
20                   dj-change="validate_field" />
21            {% if form.password.errors %}
22            <p class="error">{{ form.password.errors.0 }}</p>
23            {% endif %}
24            <p class="help">At least 8 characters</p>
25        </div>
26
27        <button type="submit">Sign Up</button>
28
29        {% if success_message %}
30        <p class="success">✓ {{ success_message }}</p>
31        {% endif %}
32    </form>
33</div>

Sortable Data Table

Server-Side

Interactive table with search and sorting

Name
Price
Stock
27" Monitor $399 12
Laptop Pro $1299 42
Mechanical Keyboard $89 23
USB-C Hub $45 78
Wireless Mouse $29 156

Search & sort execute search() and sort() on server

views.py
 1from djust import LiveView, event_handler
 2from .models import Product
 3
 4class ProductTableView(LiveView):
 5    template_name = 'product_table.html'
 6
 7    def mount(self, request):
 8        """Initialize table view"""
 9        self._products = Product.objects.all()
10        self.search_query = ""
11        self.sort_by = "name"
12        self.sort_order = "asc"
13
14    @event_handler
15    def search(self, value: str = "", **kwargs):
16        """Search products by name"""
17        self.search_query = value
18        self._refresh_products()
19
20    @event_handler
21    def sort(self, field: str = None, **kwargs):
22        """Sort products by field"""
23        if field == self.sort_by:
24            # Toggle order
25            self.sort_order = "desc" if self.sort_order == "asc" else "asc"
26        else:
27            self.sort_by = field
28            self.sort_order = "asc"
29        self._refresh_products()
30
31    def _refresh_products(self):
32        """Refresh product list with filters"""
33        queryset = Product.objects.all()
34
35        # Apply search filter
36        if self.search_query:
37            queryset = queryset.filter(
38                name__icontains=self.search_query
39            )
40
41        # Apply sorting
42        order_prefix = "-" if self.sort_order == "desc" else ""
43        queryset = queryset.order_by(f"{order_prefix}{self.sort_by}")
44
45        self._products = queryset
46
47    def get_context_data(self, **kwargs):
48        self.products = self._products  # JIT serialization
49        context = super().get_context_data(**kwargs)
50        context.update({
51            'search_query': self.search_query,
52            'sort_by': self.sort_by,
53            'sort_order': self.sort_order
54        })
55        return context
template.html
 1<div class="product-table">
 2    <h2>Products</h2>
 3
 4    <input type="text"
 5           dj-input="search"
 6           value="{{ search_query }}"
 7           placeholder="Search products..." />
 8
 9    <table>
10        <thead>
11            <tr>
12                <th dj-click="sort" data-field="name">
13                    Name
14                    {% if sort_by == 'name' %}
15                        {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}
16                    {% endif %}
17                </th>
18                <th dj-click="sort" data-field="price">
19                    Price
20                    {% if sort_by == 'price' %}
21                        {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}
22                    {% endif %}
23                </th>
24                <th dj-click="sort" data-field="stock">
25                    Stock
26                    {% if sort_by == 'stock' %}
27                        {% if sort_order == 'asc' %}↑{% else %}↓{% endif %}
28                    {% endif %}
29                </th>
30            </tr>
31        </thead>
32        <tbody>
33            {% for product in products %}
34            <tr>
35                <td>{{ product.name }}</td>
36                <td>${{ product.price }}</td>
37                <td>{{ product.stock }}</td>
38            </tr>
39            {% endfor %}
40        </tbody>
41    </table>
42</div>

Live Search with Debounce

Server-Side

Instant search filtering with debounced input

Alice Johnson

alice@example.com

Admin

Bob Smith

bob@example.com

Editor

Carol White

carol@example.com

Viewer

Dave Brown

dave@example.com

Editor

Eve Davis

eve@example.com

Admin

Frank Miller

frank@example.com

Viewer

Grace Lee

grace@example.com

Editor

Hank Wilson

hank@example.com

Viewer

dj-debounce="300" waits 300ms before calling server

views.py
 1from djust import LiveView, event_handler
 2
 3class SearchView(LiveView):
 4    template_name = 'search.html'
 5
 6    USERS = [
 7        {"name": "Alice Johnson", "email": "alice@example.com", "role": "Admin"},
 8        {"name": "Bob Smith", "email": "bob@example.com", "role": "Editor"},
 9        {"name": "Carol White", "email": "carol@example.com", "role": "Viewer"},
10        {"name": "Dave Brown", "email": "dave@example.com", "role": "Editor"},
11        {"name": "Eve Davis", "email": "eve@example.com", "role": "Admin"},
12    ]
13
14    def mount(self, request):
15        self.query = ""
16        self.filtered_users = self.USERS
17
18    @event_handler
19    def search_users(self, value: str = "", **kwargs):
20        """Filter users by name or email"""
21        self.query = value
22        q = value.lower()
23        self.filtered_users = [
24            u for u in self.USERS
25            if q in u["name"].lower() or q in u["email"].lower()
26        ] if q else self.USERS
27
28    def get_context_data(self, **kwargs):
29        return {
30            'query': self.query,
31            'filtered_users': self.filtered_users,
32        }
template.html
 1<div class="search-app">
 2    <input type="text"
 3           dj-input="search_users"
 4           dj-debounce="300"
 5           value="{{ query }}"
 6           placeholder="Search users..." />
 7
 8    <div class="results">
 9        {% for user in filtered_users %}
10        <div class="user-card">
11            <strong>{{ user.name }}</strong>
12            <span>{{ user.email }}</span>
13            <span class="role">{{ user.role }}</span>
14        </div>
15        {% empty %}
16        <p>No users found.</p>
17        {% endfor %}
18    </div>
19</div>

Chat Room (Simulated)

Server-Side

Real-time message list with server-side state

Chat Room

No messages yet. Say hello!

Messages stored in server state via send_message()

views.py
 1from djust import LiveView, event_handler
 2from datetime import datetime
 3
 4class ChatView(LiveView):
 5    template_name = 'chat.html'
 6
 7    def mount(self, request):
 8        self.messages = []
 9        self.username = "You"
10
11    @event_handler
12    def send_message(self, text: str = "", **kwargs):
13        """Add a message to the chat"""
14        if text.strip():
15            self.messages.append({
16                'user': self.username,
17                'text': text,
18                'time': datetime.now().strftime('%H:%M'),
19            })
20
21    @event_handler
22    def clear_chat(self, **kwargs):
23        """Clear all messages"""
24        self.messages = []
25
26    def get_context_data(self, **kwargs):
27        return {'messages': self.messages}
template.html
 1<div class="chat-app">
 2    <div class="messages">
 3        {% for msg in messages %}
 4        <div class="message">
 5            <strong>{{ msg.user }}</strong>
 6            <span class="time">{{ msg.time }}</span>
 7            <p>{{ msg.text }}</p>
 8        </div>
 9        {% empty %}
10        <p class="empty">No messages yet.</p>
11        {% endfor %}
12    </div>
13
14    <form dj-submit="send_message">
15        <input type="text" name="text"
16               placeholder="Type a message..." />
17        <button type="submit">Send</button>
18    </form>
19    <button dj-click="clear_chat">Clear</button>
20</div>

Drag-and-Drop Kanban

Server-Side

Drag cards between columns — HTML5 drag API + server state

todo

2

Design homepage

design AJ

Write unit tests

backend BS

doing

2

Build REST API

backend CW

Set up CI pipeline

devops AJ

done

1

Project scaffolding

devops BS

Drag cards between columns — sendEvent() bridges HTML5 drag & drop to server

views.py
 1from djust import LiveView, event_handler
 2
 3class KanbanView(LiveView):
 4    template_name = 'kanban.html'
 5
 6    def mount(self, request):
 7        self.columns = {
 8            'todo': [
 9                {'id': 1, 'text': 'Design homepage'},
10                {'id': 2, 'text': 'Write tests'},
11            ],
12            'doing': [
13                {'id': 3, 'text': 'Build API'},
14            ],
15            'done': [],
16        }
17        self.next_id = 4
18
19    @event_handler
20    def move_card(self, card_id: int = None,
21                  target: str = "", **kwargs):
22        """Move card to another column"""
23        for col, cards in self.columns.items():
24            card = next((c for c in cards if c['id'] == card_id), None)
25            if card:
26                cards.remove(card)
27                self.columns[target].append(card)
28                break
29
30    @event_handler
31    def add_card(self, column: str = "", text: str = "", **kwargs):
32        """Add new card to column"""
33        if text.strip():
34            self.columns[column].append({
35                'id': self.next_id, 'text': text
36            })
37            self.next_id += 1
38
39    def get_context_data(self, **kwargs):
40        return {'columns': self.columns}
template.html
 1<div class="kanban-board">
 2    {% for col in columns %}
 3    <div class="dropzone" data-col="{{ col.name }}">
 4        <h3>{{ col.name|title }}</h3>
 5        {% for card in col.cards %}
 6        <div class="card" draggable="true"
 7             data-card-id="{{ card.id }}">
 8            {{ card.text }}
 9        </div>
10        {% endfor %}
11    </div>
12    {% endfor %}
13</div>
14<script>
15// HTML5 drag & drop → server event
16zone.addEventListener('drop', (e) => {
17    const cardId = e.dataTransfer.getData('text');
18    djust.liveViewInstance.sendEvent('move_card', {
19        card_id: parseInt(cardId),
20        target: zone.dataset.col
21    });
22});
23</script>

Infinite Scroll Feed

Server-Side

Load more items on demand with server-side pagination

Post #1

This is the excerpt for post number 1. It contains interesting content worth reading.

Post #2

This is the excerpt for post number 2. It contains interesting content worth reading.

Post #3

This is the excerpt for post number 3. It contains interesting content worth reading.

Post #4

This is the excerpt for post number 4. It contains interesting content worth reading.

Post #5

This is the excerpt for post number 5. It contains interesting content worth reading.

load_more() increments page counter on server

views.py
 1from djust import LiveView, event_handler
 2
 3class FeedView(LiveView):
 4    template_name = 'feed.html'
 5    PAGE_SIZE = 5
 6
 7    ITEMS = [f"Post #{i}: Lorem ipsum content..." for i in range(1, 51)]
 8
 9    def mount(self, request):
10        self.page = 1
11        self.feed_items = self.ITEMS[:self.PAGE_SIZE]
12        self.has_more = len(self.ITEMS) > self.PAGE_SIZE
13
14    @event_handler
15    def load_more(self, **kwargs):
16        """Load next page of items"""
17        self.page += 1
18        end = self.page * self.PAGE_SIZE
19        self.feed_items = self.ITEMS[:end]
20        self.has_more = end < len(self.ITEMS)
21
22    def get_context_data(self, **kwargs):
23        return {
24            'feed_items': self.feed_items,
25            'has_more': self.has_more,
26        }
template.html
 1<div class="feed">
 2    {% for item in feed_items %}
 3    <div class="feed-item">
 4        <p>{{ item }}</p>
 5    </div>
 6    {% endfor %}
 7
 8    {% if has_more %}
 9    <button dj-click="load_more" class="load-more">
10        Load More
11    </button>
12    {% else %}
13    <p class="end">You've reached the end!</p>
14    {% endif %}
15</div>

Multi-Step Wizard Form

Server-Side

Step-by-step form with validation and navigation

Step state + validation all managed server-side

views.py
 1from djust import LiveView, event_handler
 2
 3class WizardView(LiveView):
 4    template_name = 'wizard.html'
 5
 6    def mount(self, request):
 7        self.step = 1
 8        self.data = {'name': '', 'email': '', 'plan': ''}
 9        self.errors = {}
10        self.submitted = False
11
12    @event_handler
13    def wizard_next(self, **kwargs):
14        """Validate and advance to next step"""
15        if self.step == 1 and not kwargs.get('name', '').strip():
16            self.errors = {'name': 'Name is required'}
17            return
18        if self.step == 2 and '@' not in kwargs.get('email', ''):
19            self.errors = {'email': 'Valid email required'}
20            return
21        self.data.update(kwargs)
22        self.errors = {}
23        self.step += 1
24
25    @event_handler
26    def wizard_prev(self, **kwargs):
27        """Go back one step"""
28        if self.step > 1:
29            self.step -= 1
30
31    @event_handler
32    def wizard_submit(self, **kwargs):
33        """Submit the wizard form"""
34        self.data.update(kwargs)
35        self.submitted = True
36
37    def get_context_data(self, **kwargs):
38        return {
39            'step': self.step,
40            'wizard_data': self.data,
41            'wizard_errors': self.errors,
42            'submitted': self.submitted,
43        }
template.html
 1<div class="wizard">
 2    <div class="steps">
 3        Step {{ step }} of 3
 4    </div>
 5
 6    {% if submitted %}
 7    <div class="success">
 8        <p>Submitted! Name: {{ wizard_data.name }}</p>
 9    </div>
10    {% elif step == 1 %}
11    <form dj-submit="wizard_next">
12        <input name="name" placeholder="Your name"
13               value="{{ wizard_data.name }}" />
14        {% if wizard_errors.name %}
15        <p class="error">{{ wizard_errors.name }}</p>
16        {% endif %}
17        <button type="submit">Next &rarr;</button>
18    </form>
19    {% elif step == 2 %}
20    <form dj-submit="wizard_next">
21        <input name="email" placeholder="Your email"
22               value="{{ wizard_data.email }}" />
23        {% if wizard_errors.email %}
24        <p class="error">{{ wizard_errors.email }}</p>
25        {% endif %}
26        <button dj-click="wizard_prev">&larr; Back</button>
27        <button type="submit">Next &rarr;</button>
28    </form>
29    {% elif step == 3 %}
30    <form dj-submit="wizard_submit">
31        <select name="plan">
32            <option value="free">Free</option>
33            <option value="pro">Pro</option>
34        </select>
35        <button dj-click="wizard_prev">&larr; Back</button>
36        <button type="submit">Submit</button>
37    </form>
38    {% endif %}
39</div>

Sortable Table + Pagination

Server-Side

Paginated data table with configurable page size

Name Category Value
Item 1 Books 17
Item 2 Clothing 34
Item 3 Electronics 51
Item 4 Books 68
Item 5 Clothing 85
Per page:
1 / 6

Pagination slicing computed server-side on each request

views.py
 1from djust import LiveView, event_handler
 2
 3class PaginatedTableView(LiveView):
 4    template_name = 'paginated_table.html'
 5
 6    DATA = [
 7        {"name": f"Item {i}", "category": ["A","B","C"][i%3],
 8         "value": i * 17 % 100}
 9        for i in range(1, 31)
10    ]
11
12    def mount(self, request):
13        self.page = 1
14        self.per_page = 5
15
16    @event_handler
17    def change_page(self, page: int = 1, **kwargs):
18        self.page = max(1, page)
19
20    @event_handler
21    def change_per_page(self, size: int = 5, **kwargs):
22        self.per_page = size
23        self.page = 1
24
25    def get_context_data(self, **kwargs):
26        start = (self.page - 1) * self.per_page
27        end = start + self.per_page
28        total_pages = -(-len(self.DATA) // self.per_page)
29        return {
30            'rows': self.DATA[start:end],
31            'page': self.page,
32            'total_pages': total_pages,
33            'per_page': self.per_page,
34        }
template.html
 1<div class="paginated-table">
 2    <table>
 3        <thead>
 4            <tr>
 5                <th>Name</th>
 6                <th>Category</th>
 7                <th>Value</th>
 8            </tr>
 9        </thead>
10        <tbody>
11            {% for row in rows %}
12            <tr>
13                <td>{{ row.name }}</td>
14                <td>{{ row.category }}</td>
15                <td>{{ row.value }}</td>
16            </tr>
17            {% endfor %}
18        </tbody>
19    </table>
20    <div class="pagination">
21        <button dj-click="change_page"
22                data-page="{{ page|add:-1 }}"
23                {% if page <= 1 %}disabled{% endif %}>
24            Prev
25        </button>
26        <span>Page {{ page }} of {{ total_pages }}</span>
27        <button dj-click="change_page"
28                data-page="{{ page|add:1 }}"
29                {% if page >= total_pages %}disabled{% endif %}>
30            Next
31        </button>
32    </div>
33</div>

Temperature Converter

Server-Side

Two-way reactive binding between Celsius and Fahrenheit

°C

=

°F

Two-way binding: each input calls a server handler that updates both values

views.py
 1from djust import LiveView, event_handler
 2
 3class ConverterView(LiveView):
 4    template_name = 'converter.html'
 5
 6    def mount(self, request):
 7        self.celsius = 0
 8        self.fahrenheit = 32
 9
10    @event_handler
11    def update_celsius(self, value: str = "0", **kwargs):
12        try:
13            c = float(value)
14            self.celsius = c
15            self.fahrenheit = round(c * 9/5 + 32, 1)
16        except ValueError:
17            pass
18
19    @event_handler
20    def update_fahrenheit(self, value: str = "32", **kwargs):
21        try:
22            f = float(value)
23            self.fahrenheit = f
24            self.celsius = round((f - 32) * 5/9, 1)
25        except ValueError:
26            pass
27
28    def get_context_data(self, **kwargs):
29        return {
30            'celsius': self.celsius,
31            'fahrenheit': self.fahrenheit,
32        }
template.html
 1<div class="converter">
 2    <div class="field">
 3        <label>Celsius</label>
 4        <input type="number"
 5               dj-change="update_celsius"
 6               value="{{ celsius }}" />
 7    </div>
 8
 9    <span class="arrow">&harr;</span>
10
11    <div class="field">
12        <label>Fahrenheit</label>
13        <input type="number"
14               dj-change="update_fahrenheit"
15               value="{{ fahrenheit }}" />
16    </div>
17</div>

Polling Dashboard (Simulated)

Server-Side

Dashboard cards with randomized metrics on refresh

Users

507

Revenue

$5968

Orders

64

Uptime

99.23%

refresh_metrics() generates new random data on server

views.py
 1from djust import LiveView, event_handler
 2import random
 3
 4class DashboardView(LiveView):
 5    template_name = 'dashboard.html'
 6
 7    def mount(self, request):
 8        self._generate_metrics()
 9
10    def _generate_metrics(self):
11        self.metrics = {
12            'users': random.randint(100, 999),
13            'revenue': random.randint(1000, 9999),
14            'orders': random.randint(10, 200),
15            'uptime': round(random.uniform(99.0, 99.99), 2),
16        }
17
18    @event_handler
19    def refresh_metrics(self, **kwargs):
20        """Simulate polling for new data"""
21        self._generate_metrics()
22
23    def get_context_data(self, **kwargs):
24        return {'metrics': self.metrics}
template.html
 1<div class="dashboard">
 2    <div class="grid">
 3        <div class="card">
 4            <h4>Users</h4>
 5            <p class="metric">{{ metrics.users }}</p>
 6        </div>
 7        <div class="card">
 8            <h4>Revenue</h4>
 9            <p class="metric">${{ metrics.revenue }}</p>
10        </div>
11        <div class="card">
12            <h4>Orders</h4>
13            <p class="metric">{{ metrics.orders }}</p>
14        </div>
15        <div class="card">
16            <h4>Uptime</h4>
17            <p class="metric">{{ metrics.uptime }}%</p>
18        </div>
19    </div>
20    <button dj-click="refresh_metrics">
21        Refresh Data
22    </button>
23</div>

Ready to Build Your Own?

Install djust locally to start building real-time applications with these patterns.