Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Rust on the client. Less than 800 lines of code. No dependencies.

License

NotificationsYou must be signed in to change notification settings

LiveDuo/tinyweb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build the client side with Rust! Backend agnostic. Less than 800 lines of code.

What's TinyWeb?

TinyWeb is a toolkit for building web applications focused on both correctness and simplicity.

Enables client-side applications to be built in pure Rust, similar to backend applications, leveraging the language strict type system and great built-in tooling. Has a tiny footprint with less than 800 lines of code, has no build step and no external dependencies.

Features

  • No Javascript
  • No macros
  • No dependencies
  • No build step
  • Just HTML & Rust (Wasm)

Note: No build step besidescargo build

Getting Started

Use the starter project

Tutorial

Create a new project

  1. Create a new Rust project withcargo new tinyweb-example --lib. Addcrate-type =["cdylib"] inCargo.toml and install the crate withcargo add tinyweb --git https://github.com/LiveDuo/tinyweb.

  2. Update thesrc/lib.rs:

use tinyweb::element::El;use tinyweb::invoke::Js;fncomponent() ->El{El::new("div").child(El::new("button").text("print").on("click",move |_|{Js::invoke("alert('hello browser')",&[]);}))}#[no_mangle]pubfnmain(){let body =Js::invoke("return document.querySelector('body')",&[]).to_ref().unwrap();component().mount(&body);}
  1. Create anindex.html in a newpublic folder:
<!DOCTYPE html><html><head><metacharset="utf-8"><scriptsrc="https://cdn.jsdelivr.net/gh/LiveDuo/tinyweb/src/js/main.js"></script><scripttype="application/wasm"src="client.wasm"></script></head><body></body></html>
  1. Build the project withcargo build --target wasm32-unknown-unknown -r. Thencp target/wasm32-unknown-unknown/release/*.wasm public/client.wasm to get the.wasm in the right place and serve thepublic folder with any static http server.

How it works

Initialization: Each project built with TinyWeb has 3 components, anindex.html, a staticmain.js and aclient.wasm file compiled from Rust withcargo build --target wasm32-unknown-unknown -r. These files can be served with any static HTTP server. When the website is visited, theindex.html file loads themain.js file which registers aDOMContentLoaded event listener. When the page finishes loading, the listener is triggered whichcalls themain function in the wasm file (usually making the initial DOM rendering and registering event listeners).

Browser APIs: When a Rust function wants to invoke a browser API, it uses the__invoke function internally, which in turn calls itscounterpart in Javascript.

Callbacks: When a listener is registered in Rust, it takes a callback function as a parameter and that function is stored inCALLBACK_HANDLERS. Every time the callback is triggered, thehandle_callback function is called which executes the callback function that was stored earlier.

How to's & guides

Browser APIs

use tinyweb::invoke::Js;Js::invoke("alert('hello browser')",&[]);

Check it outhere

Reactivity and Signals

use tinyweb::signals::Signal;use tinyweb::element::El;let signal_count =Signal::new(0);El::new("button").text("add").on("click",move |_|{let count = signal_count.get() +1;    signal_count.set(count);});

Check it outhere

Router support

use tinyweb::router::{Page,Router};thread_local!{pubstaticROUTER:RefCell<Router> =RefCell::new(Router::default());}// initialize routerlet pages =&[Page::new("/page1",page_component())];ROUTER.with(|s|{*s.borrow_mut() =Router::new("body", pages);});// navigate to routeROUTER.with(|s|{ s.borrow().navigate("/page1");});

Check it outhere

Async Support

use tinyweb::runtime::Runtime;use tinyweb::invoke::Js;Runtime::block_on(asyncmove{Runtime::promise("window.setTimeout({},{})",move |c|vec![c.into(),1_000.into()]).await;Js::invoke("alert('timer')");});

Check it outhere

Roadmap

Components & Utilities

While this library tries to be minimal and has no dependencies the reality in web development is using libraries and ready-made components especially for a few slightly annoying tasks. Here are some ideas for commonly used utilities and UI components. Utilities can be included in theexamples folder while components can be stored in a newcomponents folder in this repo.

Commonly used utilities
  • Drag & drop / resize
  • File upload
  • Markdown rendering
Commonly used components
  • Table components
  • Modals, tooltips and toasts
  • Date / time pickers
  • Chart / visualization

Benchmarks & Profiling

Need benchmarks to see how this library performs against other Rust web frameworks but also against different Javascript frameworks. Need also profiling to evaluate if there are memory leaks in either Rust side or Javascript side of the library and to figure out if the compiled WASM size can be reduced further.

Static analysis

Right nowinvoke calls to the browser APIs are not type safe. Could usewebidl interfaces to do static analysis on the Javascript code againstinvoke parameters.

Backstory

Show

For quite some time, I couldn't decide if I like Typescript or not. On one hand, it offers stronger typing than pure JavaScript, providing more confidence in the code; but on the other hand, it comes with a heavy build system that complicates things and makes debugging significantly harder.

When I had to build an application where I really cared about correctness, I realized how much I didn't trust Typescript even for what's designed to do and I tried different Rust based web frameworks instead. While these frameworks alleviated correctness concerns, they introduced significant complexity, requiring hundreds of dependencies just to get started. For reference,leptos depends on 231 crates and its development toolcargo-leptos depends on another 485 crates.

Many of these dependencies come from thewasm-bindgen crate, which generates Rust bindings for browser APIs and the JavaScript glue code needed for these calls and is used almost universally by Rust based web frameworks as a lower level building block for accessing browser APIs.

Yet, using this crate is not the only way to interact with browser APIs and many applications could benefit from a different tool that makes different tradeoffs. In particular, many applications might benefit from simplicity and ease of debugging, I know the application I'm building probably would.

So, I set out to build a web framework that allows to build client side applications with Rust and has minimal footprint. The result isTinyWeb, a client side Rust framework built in <800 lines of code.

Credits

Credits toRichard Anaya for his work onweb.rs that provided ideas to practical challenges onasync support. Also, toGreg Johnston forhis videos that show how to use Solid.js-like signals in Rust.

About

Rust on the client. Less than 800 lines of code. No dependencies.

Resources

License

Stars

Watchers

Forks

Contributors3

  •  
  •  
  •  

[8]ページ先頭

©2009-2025 Movatter.jp