• Stars
    star
    200
  • Rank 195,325 (Top 4 %)
  • Language
    Rust
  • Created about 3 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Real-time user experiences with server-rendered HTML

axum-live-view

axum-live-view allows you to build rich, real-time experiences with server-rendered HTML. This is done entirely in Rust - no JavaScript or WASM needed.

Basically Phoenix LiveView but for axum.

🚨 BIG SCARY WARNING 🚨

This project is still very much work in progress. Everything is subject to change and you shouldn't use this for anything serious.

Example usage

This is what using axum-live-view looks like.

use axum::{response::IntoResponse, routing::get, Router};
use axum_live_view::{
    event_data::EventData, html, live_view::Updated, Html, LiveView, LiveViewUpgrade,
};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;

#[tokio::main]
async fn main() {
    // A normal axum router...
    let app = Router::new()
        .route("/", get(root))
        // Use a precompiled and minified build of axum-live-view's JavaScript.
        // This is the easiest way to get started. Integration with bundlers
        // is of course also possible.
        .route("/assets/live-view.js", axum_live_view::precompiled_js());

    // ...that we run like any other axum app
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// Our handler function for `GET /`
async fn root(
    // `LiveViewUpgrade` is an extractor that accepts both regular requests and
    // WebSocket upgrade requests. If it receives a regular request it will
    // render your live view's HTML and return a regular static response. This
    // leads to good SEO and fast first paint.
    //
    // axum-live-view's JavaScript client will then call this endpoint a second
    // time to establish a WebSocket connection at which point your view will be
    // spawned in an async task. Events from the browser and HTML diffs from
    // your view will then be sent over the WebSocket connection.
    //
    // If the WebSocket connection breaks (or your view crashes) the JavaScript
    // client will call this endpoint again to establish a new connection and
    // a new instance of your view is created.
    //
    // The task running the old view automatically stops when the WebSocket is
    // closed.
    live: LiveViewUpgrade,
) -> impl IntoResponse {
    // `Counter` is our live view and we initialize it with the default values.
    let counter = Counter::default();

    live.response(|embed_live_view| {
        html! {
            <!DOCTYPE html>
            <html>
                <head>
                </head>
                <body>
                    // Embed our live view into the HTML template. This will render the
                    // view and include the HTML in the response, leading to good SEO
                    // and fast first paint.
                    { embed_live_view.embed(counter) }

                    // Load the JavaScript. This will automatically initialize live view
                    // connections.
                    <script src="/assets/live-view.js"></script>
                </body>
            </html>
        }
    })
}

// Our live view is just a regular Rust struct...
#[derive(Default)]
struct Counter {
    count: u64,
}

// ...that implements the `LiveView` trait.
impl LiveView for Counter {
    // This is the type of update messages our HTML contains. They will be sent
    // to the view in the `update` method
    type Message = Msg;

    // Update the view based on which message it receives.
    //
    // `EventData` contains data from the event that happened in the
    // browser. This might be values of input fields or which key was pressed in
    // a keyboard event.
    fn update(
        mut self,
        msg: Msg,
        data: Option<EventData>,
    ) -> Updated<Self> {
        match msg {
            Msg::Increment => {
                self.count += 1;
            }
            Msg::Decrement => {
                if self.count > 0 {
                    self.count -= 1;
                }
            }
        }

        Updated::new(self)
    }

    // Render the live view into an HTML template. This function is called during
    // the initial render in `LiveViewManager::embed` and for each subsequent
    // update.
    //
    // The HTML is diff'ed on the server and only minimal deltas are sent over
    // the wire. The browser then builds the full HTML template and efficiently
    // updates the DOM.
    fn render(&self) -> Html<Self::Message> {
        html! {
            <div>
                "Counter value: "
                // Embed dynamic Rust values into the HTML.
                //
                // `if`, `for`, and `match` are also supported.
                { self.count }
            </div>

            <div>
                // Elements with the `axm-click` attribute will send an update message
                // to the view which calls `update` after which the view is
                // re-rendered.
                <button axm-click={ Msg::Increment }>"+"</button>
                <button axm-click={ Msg::Decrement }>"-"</button>
            </div>
        }
    }

    // The `LiveView` trait also has a `mount` method that is called when a new
    // WebSocket connects. This can be used to perform auth, load data that
    // isn't needed for the first response, and spawn a task that can send
    // messages to the view itself from other parts of the application.
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Msg {
    Increment,
    Decrement,
}

More Repositories

1

todo-or-die

TODOs you cannot forget!
Rust
585
star
2

juniper-from-schema

Schema first GraphQL in Rust with Juniper
Rust
231
star
3

extend

Create extensions for types you don't own with extension traits but without the boilerplate
Rust
124
star
4

graphql-app-example

A complete example how to setup a Rust GraphQL web server
Rust
104
star
5

witter

Twitter clone we're building live on twitch.tv/davidpdrsn
Rust
86
star
6

assert-json-diff

Easily compare two JSON values and get great output
Rust
79
star
7

juniper-eager-loading

Library for avoiding N+1 query bugs with Juniper
Rust
68
star
8

dotfiles

My dotfiles
Vim Script
59
star
9

vim-spectacular

Run tests easily in any language, with any framework
Ruby
31
star
10

axum-tungstenite

WebSocket connections for axum directly using tungstenite
Rust
24
star
11

axum-flash

Flash messages for axum
Rust
23
star
12

diesel-factories

Test factories for Diesel
Rust
23
star
13

axum-typed-websockets

axum::extract::ws with type safe messages
Rust
20
star
14

robin

[UNMAINTAINED] Background jobs for Rust
Rust
18
star
15

git-remove-merged-branches

CLI to remove git branches whose PR has been merged
Rust
16
star
16

i18n_codegen

i18n library for Rust with compile time checks
Rust
15
star
17

dilemma

[Experimental]: SQL query builder favouring composition over compile time safety
Rust
14
star
18

cargo-public-api-crates

Cargo subcommand to find crates in your public API
Rust
12
star
19

tower-hyper-http-body-compat

Adapters between hyper 0.14-1.0, http-body 0.4-1.0, and tower-service 0.3.
Rust
11
star
20

CardistryIO

The cardistry community of the future
Ruby
8
star
21

hashtag-rs

Hashtag parser
Rust
7
star
22

axum-resource

Rust
6
star
23

sns-push-notifications

Library for sending iOS and Android push notifications with AWS SNS
Rust
6
star
24

tokio-console-web

Prototype web UI for tokio-console
Rust
6
star
25

load-dotenv

This is a small procedural macro to load your .env file at compile time
Rust
5
star
26

git-branch-picker

Fuzzy search for git branches
Rust
5
star
27

json-parser

JSON parser built live on stream
Rust
5
star
28

vim-leaderboard

Keep track of which Vim leader commands you run
Vim Script
4
star
29

delete-git-branches

CLI to delete old Git branches
Rust
3
star
30

vim-smart-commandt

Only flush Command-T cache when you have to
Vim Script
3
star
31

smell-spec

A small testing framework for SML inspired by RSpec
Standard ML
3
star
32

Java-Style-Guide

A Java style guide for people studying Object Oriented Programming and Design at Copenhagen University
Java
3
star
33

git-prompt

Print something like `[master@484c4c9670bb2]` for prompt
Rust
3
star
34

serializers

Easily create different JSON representations of the same type.
Rust
2
star
35

axum-extractor-config

Rust
2
star
36

Socially-fitter

Let's get fit together (school project)
PHP
2
star
37

klee-presentation

C
2
star
38

Vim-normal-mode-commands

All of Vims normale mode commands explained briefly
2
star
39

smart-pwd

Shortest unique pwd
Rust
1
star
40

lang

A silly programming language
Rust
1
star
41

Ten-Moves

iOS app I'm working on
Objective-C
1
star
42

tower-pipeline

A Tower Service combinator that "pipelines" two services
Rust
1
star
43

memosine

Toy SQL database built from scratch
Rust
1
star
44

markdown-live-preview

Live preview of a markdown file
Rust
1
star
45

oops-lang

A silly programming language
Rust
1
star
46

leaderboard

Parse a vimrc and find all the leader commands
Ruby
1
star
47

axum-handle-error-slow-compile

Reproduction of slow axum compile
Rust
1
star
48

git-git

Rewrite of https://github.com/tonsser/api-git-scripts in Rust
Rust
1
star
49

juniper-look-ahead-with-fragments-error

Rust
1
star
50

test-wrapper

Hack to run Rust tests in a file or on a line
Rust
1
star
51

vim-notable

Easy per-project notes
Vim Script
1
star
52

trust-me-its-minitest

Minitest implemented on top of Rspec
Ruby
1
star