• Stars
    star
    186
  • Rank 200,319 (Top 5 %)
  • Language
    Rust
  • Created over 2 years ago
  • Updated 7 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
583
star
2

juniper-from-schema

Schema first GraphQL in Rust with Juniper
Rust
227
star
3

extend

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

graphql-app-example

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

witter

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

assert-json-diff

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

juniper-eager-loading

Library for avoiding N+1 query bugs with Juniper
Rust
67
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

diesel-factories

Test factories for Diesel
Rust
23
star
11

axum-tungstenite

WebSocket connections for axum directly using tungstenite
Rust
22
star
12

axum-flash

Flash messages for axum
Rust
20
star
13

robin

[UNMAINTAINED] Background jobs for Rust
Rust
18
star
14

git-remove-merged-branches

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

axum-typed-websockets

axum::extract::ws with type safe messages
Rust
17
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

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
19

cargo-public-api-crates

Cargo subcommand to find crates in your public API
Rust
11
star
20

bae

A Rust proc-macro attribute parser inspired by Darling
Rust
9
star
21

CardistryIO

The cardistry community of the future
Ruby
8
star
22

hashtag-rs

Hashtag parser
Rust
7
star
23

axum-resource

Rust
6
star
24

load-dotenv

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

git-branch-picker

Fuzzy search for git branches
Rust
6
star
26

sns-push-notifications

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

tokio-console-web

Prototype web UI for tokio-console
Rust
6
star
28

json-parser

JSON parser built live on stream
Rust
5
star
29

vim-leaderboard

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

delete-git-branches

CLI to delete old Git branches
Rust
4
star
31

git-prompt

Print something like `[master@484c4c9670bb2]` for prompt
Rust
4
star
32

vim-smart-commandt

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

smell-spec

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

Java-Style-Guide

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

serializers

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

axum-extractor-config

Rust
2
star
37

Socially-fitter

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

klee-presentation

C
2
star
39

Vim-normal-mode-commands

All of Vims normale mode commands explained briefly
2
star
40

smart-pwd

Shortest unique pwd
Rust
1
star
41

lang

A silly programming language
Rust
1
star
42

Ten-Moves

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

tower-pipeline

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

memosine

Toy SQL database built from scratch
Rust
1
star
45

markdown-live-preview

Live preview of a markdown file
Rust
1
star
46

oops-lang

A silly programming language
Rust
1
star
47

leaderboard

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

trust-me-its-minitest

Minitest implemented on top of Rspec
Ruby
1
star
49

axum-handle-error-slow-compile

Reproduction of slow axum compile
Rust
1
star
50

git-git

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

vim-notable

Easy per-project notes
Vim Script
1
star
52

juniper-look-ahead-with-fragments-error

Rust
1
star
53

test-wrapper

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