Phoenix LiveView but for Django


Reactor, a LiveView library for Django

Reactor enables you to do something similar to Phoenix framework LiveView using Django Channels.

TODO MVC demo app

What's in the box?

This is no replacement for VueJS or ReactJS, or any JavaScript but it will allow you use all the potential of Django to create interactive front-ends. This method has its drawbacks because if connection is lost to the server the components in the front-end go busted until connection is re-established. But also has some advantages, as everything is server side rendered the interface comes already with meaningful information in the first request response, you can use all the power of Django template without limitations, if connection is lost or a component crashes, the front-end will have enough information to rebuild their state in the last good known state.

Installation and setup

Reactor requires Python >=3.6.

Install reactor:

pip install django-reactor

Reactor makes use of django-channels, by default this one uses an InMemory channel layer which is not capable of a real broadcasting, so you might wanna use the Redis one, take a look here: Channel Layers

Add reactor and channels to your INSTALLED_APPS before the Django applications so channels can override the runserver command.



ASGI_APPLICATION = 'project_name.asgi.application'

and modify your project_name/ file like:

import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project_name.settings')

import django

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from reactor.urls import websocket_urlpatterns

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': AuthMiddlewareStack(URLRouter(websocket_urlpatterns))

Note: Reactor since version 2, autoloads any file in your applications with the hope to find there Reactor Components so they get registered and can be instantiated.

In the templates where you want to use reactive components you have to load the reactor static files. So do something like this so the right JavaScript gets loaded:

{% load reactor %}
<!doctype html>
     {% reactor_header %}

Don't worry if you put this as early as possible, the scripts are loaded using <script defer> so they will be downloaded in parallel with the html, and then all is loaded they are executed.


  • REACTOR_AUTO_BROADCAST (default: False), when enabled will activate listeners for every time a model is created, modified or deleted, and will broadcast a message related to that modification that you can subscribe to and use to refresh your components in real-time, you can fine tune what kind of notification you want to get by turning this in a dictionary, for example:
    # model_a
    # model_a.del
    'MODEL': True,

    # model_a.1234
    'MODEL_PK': True,

    # model_b.1234.model_a_set
    # model_b.1234.model_a_set.del
    'RELATED': True,

    # model_b.1234.model_a_set
    # model_a.1234.model_b_set
    'M2M': True,
  • REACTOR_USE_HTML_DIFF (default: True), when enabled uses difflib to create diffs to patch the front-end, reducing bandwidth.
  • REACTOR_USE_HMIN (default: False), when enabled and django-hmin is installed will use it to minified the HTML of the components and save bandwidth.

Back-end APIs

Template tags and filters of reactor library

  • {% reactor_headers %}: that includes the necessary JavaScript to make this library work. ~5Kb of minified JS, compressed with gz or brotli.
  • {% component 'x-component-name' param1=1 param2=2 %}: Renders a component by its name and passing whatever parameters you put there to the Component.mount method.
  • tojson: Takes something and renders it in JSON, the ReactorJSONEncoder extends the DjangoJSONEncoder it serializes a Model instance to its id and a QuerySet as a list of ids.
  • tojson_safe: Same as tojson but does not "HTML escapes" the output.
  • then: Use as a shorthand for if, {% if expression %}print-this{% endif %} is equivalent to {{ expresssion|then:'print-this' }}.
  • ifnot: Use a shorthand for if not, {% if not expression %}print-this{% endif %} is equivalent to {{ expresssion|ifnot:'print-this' }}, and can be concatenated with then, like in: {{ expression|then:'positive'|ifnot:'negative' }}`
  • eq: Compares its arguments and returns "yes" or empty string, {{ this_thing|eq:other_thing|then:'print-this' }}.
  • cond: Allows simple conditional presence of a string: {% cond {'hidden': is_hidden } %}.
  • class: Use it to handle conditional classes: <div {% class {'nav_bar': True, 'hidden': is_hidden} %}></div>.

reactor.component module

  • Component: This is the base component you should extend.
  • AuthComponent: Extends Component and ensures the user is logged in.
  • broadcast(*names): Broadcasts the given names too all the system.
  • on_commit(function)(*args, **kwargs): Calls function with the given arguments after database commit.

Component API

  • __init__: Is responsable for the component initialization, pass what ever you need to bootstrap the component state.
  • template_name: Set the name of the template of the component.
  • extends: Tag name HTML element the component extends.
  • _subscribe(*names): Subscribes the current component to the given signal names, when one of those signals is broadcasted the component is refreshed, meaning that mount is called passing the result serialize and the component is re-rendered.
  • visit(url, action='advance', **kwargs ): Resolves the url using **kwargs, and depending on action the navigation will be advance (pushState) or replace (repalceState).
  • destroy(): Removes the component from the interface.
  • _send(_name, id=None, **kwargs): Sends a message with the name _name to the component with id, if id is None the message is sent to the current component.
  • _send_parent(_name, kwargs): Sends a message with the name _name to the parent component.

Front-end APIs

  • reactor.visit(url, {action='advance'}): if action is advance, calls window.history.replaceState, else tries to talk to Turbo or falls back to window.history.pushState or just window.location.assign.
  • reactor.send(element, event_name, args): send the event event_name with the args parameters to the HTML element. It what is used to forward user event to the back-end.

Special HTMLElement attributes

  • :keep: Prevent the value of an input from being changed across renders.
  • :override: When an input is being updated and the user has the focus there reactor by default will not update the input field value (has if it had :keep), use :override to do otherwise.
  • :once: Reactor will render this element and children once, and never update it again.
  • :focus: Sets the focus on this element after an HTML update.

Event binding in the front-end

Look at this:

  <button @click.prevent="submit">Submit</button?>

The format is @<event>[.modifier][.modifier]="event_name[ {arg1: 1, arg2: '2'}]":

  • event: is the name of the HTMLElement event: click, blur, change, keypress, keyup, keydown...
  • modifier: can be concatenated after the event name and represent actions or conditions to be met before the event execution. This is very similar as how VueJS does event binding:
    • prevent: calls event.preventDefault();
    • stop: calls (event.stopPropagation();),
    • enter, ctrl, alt, space, expects any of those keys to be press.
    • inlinejs: allows you to write your custom JavaScript in the event handler.
    • debounce: debounces the event, it needs a name and a delay in milliseconds. Example:'message'.
  • event_name: is the name of the message to be send to this component
  • The arguments can be completely omitted, or specified as a dictionary.

When the arguments are omitted, reactor serializes the form where the current element is or the current component if no form is found, and sends that as the arguments. The arguments will be always sent with the id of the current component as a parameter.

JS Hooks

These are custom events triggered by reactor in different instants of the life cycle of the component.

  • onreactor-init: Triggered on any HTML element when the component is initialized.
  • onreactor-added: Triggered on any HTML element that is added to the DOM of the component.
  • onreactor-updated: Triggered on any HTML element that is updated, after the update happens.
  • onreactor-leave: Triggered on the root element when the element had been removed from the DOM.

Event handlers in the back-end


<button @click="inc {amount: 2}">Increment</button?>

You will need an event handler in that component in the back-end:

def inc(self, amount: int):

Simple example of a counter

In your app create a template x-counter.html:

{% load reactor %}
<div {% tag_header %}>
  {{ amount }}
  <button @click="inc">+</button>
  <button @click="dec">-</button>
  <button @click="set_to {amount: 0}">reset</button>

Anatomy of a template: each component should be a custom web component that inherits from HTMLElement. They should have an id so the backend knows which instance is this one and a state attribute with the necessary information to recreate the full state of the component on first render and in case of re-connection to the back-end.

Render things as usually, so you can use full Django template language, trans, if, for and so on. Just keep in mind that the instance of the component is referred as this.

Forwarding events to the back-end: Notice that for event binding in-line JavaScript is used on the event handler of the HTML elements. How does this work? When the increment button receives a click event send(this, 'inc') is called, send is a reactor function that will look for the parent custom component and will dispatch to it the inc message, or the set_to message and its parameters {amount: 0}. The custom element then will send this message to the back-end, where the state of the component will change and then will be re-rendered back to the front-end. In the front-end morphdom (just like in Phoenix LiveView) is used to apply the new HTML.

Now let's write the behavior part of the component in

from reactor import Component

class XCounter(Component):
    template_name = 'x-counter.html'

    def __init__(self, amount: int = 0, **kwargs):
        self.amount = amount

    def inc(self):
        self.amount += 1

    def dec(self):
        self.amount -= 1

    def set_to(self, amount: int):
        self.amount = amount

Let's now render this counter, expose a normal view that renders HTML, like:

def index(request):
    return render(request, 'index.html')

And the index template being:

{% load reactor %}
<!doctype html>
     {% reactor_header %}
    {% component 'x-counter' %}

    <!-- or passing an initial state -->
    {% component 'x-counter' amount=100 %}


Don't forget to update your to call the index view.

More complex components

I made a TODO list app using models that signals from the model to the respective channels to update the interface when something gets created, modified or deleted.

This example contains nested components and some more complex interactions than a simple counter, the app is in the /tests/ directory.

Development & Contributing

Clone the repo and create a virtualenv or any other contained environment, get inside the repo directory, build the development environment and the run tests.

git clone [email protected]:edelvalle/reactor.git
cd reactor
make install
make test

If you want to run the included Django project used for testing do:

cd tests
python runserver


  • Error when connecting to the WebSocket

    Error when connecting to the WebSocket

    When loading up the sample increment/decrement code, the WebSocket seemed to never connect. This was the error I was seeing in error logs:

    File "/Users/.../Library/Caches/pypoetry/virtualenvs/django-reactor-py3.7/lib/python3.7/site-packages/reactor/", line 44, in connect
        self.scope['channel_name'] = self.channel_name
      'ReactorConsumer' object has no attribute 'channel_name'
    opened by adamghill 8
  • Cannot get subscriptions to work, _channel_name is None

    Cannot get subscriptions to work, _channel_name is None

    When I call self._subscribe on my components, nothing happens. I have stepped through the code with a debugger and it seems as if the property channel_name is always empty, so the check if _channel_name in def send_to_channel is always false for me and thus nothing happens when it's called.

    Is this a bug or am I missing something in my configuration? I was unable to figure out how the property was supposed to get its value.

    This is with the Redis channel layer, on version 2.2.1.b with Django 3.1.7 and Python 3.7

    opened by TomasLoow 4
  • Add counter to the examples

    Add counter to the examples

    @adamghill Check this out, I added the "counter" example to the test project and is working fine, can't reproduce your #3

    opened by edelvalle 2
  • Always use id when consructing Component objects

    Always use id when consructing Component objects

    Hi @edelvalle! Great module. I'm just experimenting, but I found if a component is nested inside another component, I found that id was not being set properly as it was not included in Component._build if _parent_id is set.

    Disclaimer: I've only very briefly looked at the internals and unfortunately my Python segfaults when I try to run, so apologies if this is not a useful PR.

    opened by kbni 2
  • ModuleNotFoundError: No module named 'reactor.urls'

    ModuleNotFoundError: No module named 'reactor.urls'

    I followed the README and encountered this error:

    File "/home/me/project/app/", line 34, in from reactor.urls import websocket_urlpatterns ModuleNotFoundError: No module named 'reactor.urls'

    from channels.auth import AuthMiddlewareStack
    from channels.routing import ProtocolTypeRouter, URLRouter
    from reactor.urls import websocket_urlpatterns
    application = ProtocolTypeRouter({
        'http': get_asgi_application(),
        'websocket': AuthMiddlewareStack(URLRouter(websocket_urlpatterns))

    There is no urls file in the package. Where am I supposed to get websocket_urlpatterns from?

    opened by richarddewit 2
  • Fix WebSocket connection error

    Fix WebSocket connection error

    opened by adamghill 1
  • README Updates to clarify examples

    README Updates to clarify examples

    Updating README to specify where components are created, call out Channel Layers dependency, and adjust example to have 'component' as the custom tag name.

    opened by cyface 1
  • fix typos in

    fix typos in

    fixes two typos

    opened by jandemter 1
  • Remove ? from </button>

    Remove ? from

    After reading the README I suspect it is a bug. Otherwise I am sorry for the distraction.

    opened by tanrax 1
  • Release notes/changelog?

    Release notes/changelog?

    This project has many releases. However, it is difficult to understand what is changing with each release. Please consider adding release notes or a changelog file to the repository, describing additions, improvements, and deprecations in each version.

    opened by brylie 1
  • daphne/channels update

    daphne/channels update

    Hi, just trying to setup reactor. If I make a plain python project, and just do a pip install django-reactor as recommended, I get:


    But, in your poetry.lock you have channels version = "3.0.3", and daphne = ">=3.0,<4".

    How comes this? (BTW, I didn't get the websockets part working yet... still struggling)

    opened by nerdoc 6
  • Another LiveView for django project

    Another LiveView for django project

    Hi Edy, have you checked this: ? I may find some time to try it out...

    opened by jbjuin 5
  • understanding reactor

    understanding reactor

    Let's say I use reactor to build a big table of 1,000 rows. I want to allow the user to edit 1 row. How would reactor do this? I understand morphdom can replace html content as below, but how would I change only 1 row? I see you are using the uuid4 library so I am guessing each element has an id? Does it mean every time you send an event from client to server you also send the element id?

    Another question, is it true all application state in reactor is in the actual html? I am not used to this thinking, it makes a lot of sense to me, but I am used to virtual doms so I wonder if this method has any drawbacks? I am guessing in my 1,000 rows example above the entire state would be in the <table> component, right? Does it mean that if there is a connection issue the server relies on the client html to get the latest available state and re-mount the component?

    I must say I would love to use reactor but I am a heavy flask user so I am also considering a port to flask of reactor.

    opened by acivitillo 2
  • Problem with inline script tag evaluation

    Problem with inline script tag evaluation

    Hi @edelvalle ! I just hit another pb with execution of inline script tags in reactor components.

    It is related with this:

    • Morphdom by default use innerHtml to replace content of nodes and this prevents execution of script tags (for security reasons)
    • a workaround is proposed here would you mind introducing it in reactor ? Should I propose a PR ?

    It's quite useful to easily add some full height components for instance or add any JS libraries to components.

    opened by jbjuin 0
  • Websocket path & django served in subpath

    Websocket path & django served in subpath

    Hi Eddy, I just came across an issue when serving django on a subpath. The websocket url __reactor__ is hardcoded as /__reactor__ in the frontend. And I see that you hardcoded django.setup(set_prefix=False) so that the parameter FORCE_SCRIPT_NAME is not used...

    So Django is configured to work on a subpath, let's say but reactor frontend will try to connect at and not which should be handled correctly by Django.

    Any idea on how to solve that ? A variable passed to js in the template ?

    Cheers !

    opened by jbjuin 0
  • Thank you for library, I have some questions

    Thank you for library, I have some questions

    Hello Eddy, thank you for the library!

    I am not a very experienced developer and I have several questions and problems with the library:

    1. Is it possible to render a component by link? I need this for the following case: there is a main form, and some of its fields are list of objects of another model(many-to-many relationship), I need to asynchronously add an object to this list through another form that is displayed in a new browser tab and then re-render the main form. I tried to do it in different ways, but I constantly ran into errors or nothing worked. If it is not possible to render component by link, how can I implement this functionality? If you give an example of the code, it will be generally great, but if you describe it in words, then it will be well too.
    2. send_redirect method takes url as django url name or it is string?
    3. I ran into the problem that when sending an event, the component is unmounted for some reason. What is the reason for this?

    Thanks again for work you've done!

    Waiting for your reply

    opened by hunterinious 4
  • Loading state for component

    Loading state for component

    Hi Eddy, I just pulled your new version. Awesome ! I love the @ syntax for event binding.

    Do you think it would be hard to add a "loading" state on components ? Where should I start to implement this ?


    opened by jbjuin 4
  • Great job, I have a few questions

    Great job, I have a few questions

    Hello Eddy,

    I just wanted to say "Hi" and thank you for doing this! I've been dreaming about LiveView for Django since the day it first got announced. Definitely going to play with Reactor.

    I have a few questions:

    1. What's the feature parity compared to the original Phoenix LiveView, does it implement everything or there's something missing?
    2. Have you experienced any performance drawbacks? I know that Elixir is fast, and WebSockets are super cheap on Erlang, but what about Python/Django?
    3. Do you consider this library more or less complete? Are you're planning to add more features?
    4. And sorry about a question, but why do you use coffeescript? Is there any benefit? I'm asking because I think it might be a contribution blocker to a lot of people (me including).

    Other than that, great job and thank you again!

    opened by timonweb 23
  • 1.2.0b0(Jun 24, 2019)

    Breaking changes in how to test components, for better

    Using async with reactor() as comm, you get an object that you can use to register components, send commands... doc is a PyQuery object containing the HTML rendering of the object, so you can perform checkings of the rendering of the object.

    from pytest import mark
    from channels.db import database_sync_to_async as db
    from reactor.tests import reactor
    async def test_live_components():
            x_list = comm.add_component(
                {'id': 'someid', 'showing': 'all'}
            x_todo_counter = comm.add_component(
                {'id': 'someid-counter'}
            doc = await comm.send_join(x_list)
            todo_list = doc('#someid')
            assert json.loads(todo_list.attr['state']) == comm[x_list].state
            # Add new item
            doc = await comm.send(x_list, 'add', new_item='First task')
            # There was an item crated and rendered
            assert await db(Item.objects.count)() == 1
            assert len(doc('x-todo-item')) == 1
            todo_item_id = doc('x-todo-item')[0].get('id')
            todo_item_label = doc('x-todo-item label')[0]
            assert todo_item_label.text == 'First task'
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0b0(Jun 20, 2019)


    • Component.send_redirect has a new parameter push_state=True, when set it will do a push state in the front-end.
    • In the front-end you can use push_state(url) to trigger this in the front-end.
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0b0(Jun 18, 2019)


    • Rename reactor.component.send_to_group to reactor.broadcast
    • Open the reactor channel after all HTML5 elements are created.
    • Improve reconnect timing logic.
    • When an event is sent to the back-end and the event originates from a form or inside of it, just the form data is serialized and sent to the back-end.


    • Django settings REACTOR_AUTO_BROADCAST = True will trigger broadcast updates when models instances are manipulated.
    • Add a component for authenticated user only and staff component: AuthComponent and StaffComponent
    • Add debounce function to send events to the backend.
    • Add directive reactor-once when an element is market with it, it will not be updated.
    • Add ReactorChannel.reconnect, closes the connection and reopens it.
    • Add public parameter to class inheritance, so you can decide if the element is exposed or not.
    Source code(tar.gz)
    Source code(zip)
  • 0.3.0b0(Jun 3, 2019)


    Breaking changes:

    • Now the dispatching of incoming events from the front-end are not atomic transactions. You will have to put @atomic when you want something to be atomic. The reason for this is that some events don't have to deal at all with the database because they just change the local state of the component.


    • reactor.broadcast(*names): this broadcasts the messages that you can subscribe to using Component.subscribe().
    • Component.send_redirect(url): now you can send a URL redirect to the front-end, when the component is being rendered for first time the redirect is rendered as a <meta http-equiv="refresh" content="0; url={{ url }}">, if the component is alive then the front-end controller executes the redirect.
    • AuthComponent, this component has a user attributed taken from the Component._context and on mount if the user is authenticated returns the True, if the user is not returns None and sends a redirect to settings.LOGIN_URL.
    • Serialization of arrays in the front-end, in case you have:
        <input name="query" value="q">
        <input name="persons[].name" value="a">
        <input name="persons[].name" value="b">

    This will be serialized as: {query: "q", persons: [{name: "a"}, {name: "b"}]}

    Room for improvement:

    • Proper documentation.
    • More testing.
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1b0(May 26, 2019)

    • Adds to component component send_redirect(url). Example:
    def receive_save(self, name, **kwargs): = name
    • Components are now based on div and this can be redefined by the user overwriting the extends attribute. Example:
    class MySpecialInput(Component):
        extends = 'input'
    Source code(tar.gz)
    Source code(zip)
Eddy Ernesto del Valle Pino
Co-founder and CTO at Kaiko Systems
Eddy Ernesto del Valle Pino
Serverless Python

Zappa - Serverless Python About Installation and Configuration Running the Initial Setup / Settings Basic Usage Initial Deployments Updates Rollback S

Rich Jones 11.8k Oct 22, 2021
Serverless Python

Zappa - Serverless Python About Installation and Configuration Running the Initial Setup / Settings Basic Usage Initial Deployments Updates Rollback S

Rich Jones 11.7k Feb 17, 2021
Django Ninja - Fast Django REST Framework

Django Ninja is a web framework for building APIs with Django and Python 3.6+ type hints.

Vitaliy Kucheryaviy 1.6k Oct 23, 2021
An abstract and extensible framework in python for building client SDKs and CLI tools for a RESTful API.

django-rest-client An abstract and extensible framework in python for building client SDKs and CLI tools for a RESTful API. Suitable for APIs made wit

Certego 2 Oct 22, 2021
The Web framework for perfectionists with deadlines.

Django Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. All docu

Django 60.3k Oct 22, 2021
News search API developed for the purposes of the ColdCase Project.

Saxion - Cold Case - News Search API Setup Local – Linux/MacOS Make sure you have python 3.9 and pip 21 installed. This project uses a MySQL database,

Dimitar Rangelov 3 Jul 1, 2021
A beginners course for Django

The Definitive Django Learning Platform. Getting started with Django This is the code from the course "Getting Started With Django", found on YouTube

JustDjango 190 Oct 19, 2021
A familiar HTTP Service Framework for Python.

Responder: a familiar HTTP Service Framework for Python Powered by Starlette. That async declaration is optional. View documentation. This gets you a

Taoufik 3.6k Oct 15, 2021
The little ASGI framework that shines. ?

✨ The little ASGI framework that shines. ✨ Documentation: Community: Starlette Starlet

Encode 6.2k Oct 24, 2021
The Modern And Developer Centric Python Web Framework. Be sure to read the documentation and join the Slack channel questions:

NOTE: Masonite 2.3 is no longer compatible with the masonite-cli tool. Please uninstall that by running pip uninstall masonite-cli. If you do not unin

Masonite 1.7k Oct 23, 2021
Appier is an object-oriented Python web framework built for super fast app development.

Joyful Python Web App development Appier is an object-oriented Python web framework built for super fast app development. It's as lightweight as possi

Hive Solutions 116 Oct 2, 2021
Fast, asynchronous and elegant Python web framework.

Warning: This project is being completely re-written. If you're curious about the progress, reach me on Slack. Vibora is a fast, asynchronous and eleg 5.7k Oct 15, 2021
Daniel Vaz Gaspar 3.5k Oct 22, 2021
The no-nonsense, minimalist REST and app backend framework for Python developers, with a focus on reliability, correctness, and performance at scale.

The Falcon Web Framework Falcon is a reliable, high-performance Python web framework for building large-scale app backends and microservices. It encou

Falconry 8.6k Oct 21, 2021
Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.

Read Latest Documentation - Browse GitHub Code Repository hug aims to make developing Python driven APIs as simple as possible, but no simpler. As a r

Hug API Framework 6.6k Oct 18, 2021
A micro web-framework using asyncio coroutines and chained middleware.

Growler master ' dev Growler is a web framework built atop asyncio, the asynchronous library described in PEP 3156 and added to the standard library i

null 686 Aug 12, 2021
Screaming-fast Python 3.5+ HTTP toolkit integrated with pipelining HTTP server based on uvloop and picohttpparser.

Japronto! There is no new project development happening at the moment, but it's not abandoned either. Pull requests and new maintainers are welcome. I

Paweł Piotr Przeradowski 8.5k Oct 21, 2021
FastAPI framework, high performance, easy to learn, fast to code, ready for production

FastAPI framework, high performance, easy to learn, fast to code, ready for production Documentation: Source Code: https:

Sebastián Ramírez 37.2k Oct 23, 2021
Trame let you weave various components and technologies into a Web Application solely written in Python.

Trame Trame aims to be a framework for building interactive applications using a web front-end in plain Python. Such applications can be used locally

Kitware, Inc. 6 Oct 15, 2021