#ui-framework #functional #web-ui #ui-component #web-framework #gui

consecuit

A functional web UI framework that uses the type system for hooks and more

2 unstable releases

0.2.0 Jun 25, 2021
0.1.0 Jun 24, 2021

#465 in WebAssembly


Used in consecuit_html

LGPL-2.1

73KB
1.5K SLoC

Consecuit

crates.io crates.io

An experimental functional web UI framework that uses the Rust type system for hooks and more.

How is this different from other frameworks?

Other Functional UI Frameworks:

  • Require the developer to follow the Rule of Hooks or something similar.

  • Dynamically identify components/hooks and maintain their states by counting the order in which they are called.

  • Dynamic design:

    • Components are dynamically created and mounted as their parents render.
    • Components may be mounted or unmounted depending on state.

Consecuit:

  • Automatically enforce the Rule of Hooks using the Rust type system.

  • Statically know every component that will be created and every hook call that will happen.

  • Static-first design:

    • Components are statically mounted at the start of the app and remain mounted forever by default.
    • Mounting/unmounting based on state are considered special cases available through explicitly using opt_comp, vec_comps, etc.

What does it look like?

Take a look at our TodoMVC (and see its source code).

Or if you want something simpler, here is the code for a counter.

use consecuit::prelude::*;
use consecuit_html::prelude::*;
use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
    consecuit::mount::mount_app(counter);
    Ok(())
}

fn counter(cc: ComponentBuilder, _: ()) -> impl ComponentReturn {
    let (cc, (count, setter)) = cc.hook(use_state, 0);

	let setter1 = setter.clone();
    let decrement = Callback::new(move |_ev| {
        setter1.update_with(|v| v - 1);
    });

    let increment = Callback::new(move |_ev| {
        setter.update_with(|v| v + 1);
    });
    
    cc_tree!(
		<button {html_props().onclick(decrement)}>"-"</button>
		{count.to_string()}
		<button {html_props().onclick(increment)}>"+"</button>
    )
}

There are more counter examples here (with live demo here), including one without macro and one with logic extracted into a use_counter function.

How do I start?

Follow our simple guide below:

Click to expand guide

Note: This guide is for you to get started as quickly as possible. The WASM setup part of the guide is very basic. You should read the rustwasm book later on.

  1. Initialize a new lib crate.

    cargo new --lib YOUR_CRATE_NAME_HERE
    cd YOUR_CRATE_NAME_HERE
    
  2. Add this to your Cargo.toml:

    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    wasm-bindgen = "0.2.74"
    consecuit = "0.2.0"
    consecuit_html = "0.2.0"
    
  3. Create an index.html in the root of your project with this content.

    <html>
    <head>
        <meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
    </head>
    <body>
        <script type="module">
            import init from './pkg/YOUR_CRATE_NAME_HERE.js';
            init();
        </script>
    </body>
    </html>
    

    (replace YOUR_CRATE_NAME_HERE with the name of your crate.)

  4. Install wasm-pack:

    cargo install wasm-pack
    
  5. Write your code.

    You can copy-paste the counters example above.

    Also take a look at the examples directory and the docs.

  6. Build it!

    wasm-pack build --dev --target web
    
  7. Serve it!

    # Install a simple web server.
    cargo install microserver
    # And run it!
    microserver
    

Final code is in examples/minimal_counter.

The docs have more info on creating components and hooks.

What's next?

This library is still in an early stage. A lot of features are missing. (For example, inline styling is not available right now; you need to use class names and write CSS.)

Contributions are welcomed!

This crate uses unsafe.

All publicly exposed functions are safe.

Dependencies

~11MB
~212K SLoC