base on Build terminal-themed web applications with Rust and WebAssembly. Powered by Ratatui. <p align="center">
<!-- Thanks to https://github.com/dekirisu for the logo -->
<a href="https://github.com/orhun/ratzilla"><img src="https://raw.githubusercontent.com/orhun/ratzilla/refs/heads/main/assets/ratzilla.gif" width="500"></a>
</p>
<div align="center">
[](https://github.com/orhun/ratzilla)
[](https://crates.io/crates/ratzilla)
[](https://docs.rs/ratzilla)
**Watch the conference talk:** [Bringing Terminal Aesthetics to the Web With Rust (and Vice Versa)](https://www.youtube.com/watch?v=iepbyYrF_YQ)
</div>
# Ratzilla
Build terminal-themed web applications with Rust and WebAssembly. Powered by [Ratatui].
## Quickstart
### Templates
Install [`cargo-generate`](https://github.com/cargo-generate/cargo-generate):
```shell
cargo install cargo-generate
```
Generate a new project:
```shell
cargo generate orhun/ratzilla
```
And then [serve the application on your browser](#serve) ➡️
See [templates](./templates) for more information.
### Manual Setup
Add **Ratzilla** as a dependency in your `Cargo.toml`:
```sh
cargo add ratzilla
```
Here is a minimal example:
```rust no_run
use std::{cell::RefCell, io, rc::Rc};
use ratzilla::ratatui::{
layout::Alignment,
style::Color,
widgets::{Block, Paragraph},
Terminal,
};
use ratzilla::{event::KeyCode, DomBackend, WebRenderer};
fn main() -> io::Result<()> {
let counter = Rc::new(RefCell::new(0));
let backend = DomBackend::new()?;
let terminal = Terminal::new(backend)?;
terminal.on_key_event({
let counter_cloned = counter.clone();
move |key_event| {
if key_event.code == KeyCode::Char(' ') {
let mut counter = counter_cloned.borrow_mut();
*counter += 1;
}
}
});
terminal.draw_web(move |f| {
let counter = counter.borrow();
f.render_widget(
Paragraph::new(format!("Count: {counter}"))
.alignment(Alignment::Center)
.block(
Block::bordered()
.title("Ratzilla")
.title_alignment(Alignment::Center)
.border_style(Color::Yellow),
),
f.area(),
);
});
Ok(())
}
```
Add your `index.html`. During build, `trunk` will automatically inject and initialize your Rust code (compiled to
WebAssembly) as a JavaScript module.
<details>
<summary>index.html</summary>
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/fira_code.min.css"
/>
<link data-trunk rel="rust"/>
<title>Ratzilla</title>
<style>
body {
margin: 0;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
align-content: center;
background-color: #121212;
}
pre {
font-family: "Fira Code", monospace;
font-size: 16px;
margin: 0px;
}
</style>
</head>
<body>
<!-- (optional) subscribe to the application started event -->
<script type="module">
window.addEventListener("TrunkApplicationStarted", (_) => {
// #[wasm_bindgen] functions are now bound to window.wasmBindings.*
console.log("application initialized");
});
</script>
</body>
</html>
```
</details>
And then [serve the application on your browser](#serve) ➡️
## Serve
Install [trunk] to build and serve the web application.
```sh
cargo install --locked trunk
```
Add compilation target `wasm32-unknown-unknown`:
```sh
rustup target add wasm32-unknown-unknown
```
Then serve it on your browser:
```sh
trunk serve
```
Now go to [http://localhost:8080](http://localhost:8080) and enjoy TUIs in your browser!
## Deploy
To build the WASM bundle, you can run the following command:
```sh
trunk build --release
```
Then you can serve the server from the `dist` directory.
<details>
<summary>Example Build Script</summary>
```bash
#!/bin/bash
set -euo pipefail
export HOME=/root
# Install Rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -t wasm32-unknown-unknown --profile minimal
source "$HOME/.cargo/env"
# Install trunk using binstall
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
cargo binstall --targets x86_64-unknown-linux-musl -y trunk
# Build project with trunk
trunk build --release
```
</details>
### Vercel
There is a Vercel deployment template available for Ratzilla [here](https://vercel.com/templates/other/ratzilla).
## Documentation
- [API Documentation](https://docs.rs/ratzilla)
- [Backends](https://docs.rs/ratzilla/latest/ratzilla/backend/index.html)
- [Widgets](https://docs.rs/ratzilla/latest/ratzilla/widgets/index.html)
## Examples
- [Minimal](https://github.com/orhun/ratzilla/tree/main/examples/minimal) ([Preview](https://orhun.dev/ratzilla/minimal))
- [Demo](https://github.com/orhun/ratzilla/tree/main/examples/demo) ([Preview](https://orhun.dev/ratzilla/demo))
- [Pong](https://github.com/orhun/ratzilla/tree/main/examples/pong) ([Preview](https://orhun.dev/ratzilla/pong))
- [Colors RGB](https://github.com/orhun/ratzilla/tree/main/examples/colors_rgb) ([Preview](https://orhun.dev/ratzilla/colors_rgb))
- [Animations](https://github.com/orhun/ratzilla/tree/main/examples/animations) ([Preview](https://orhun.dev/ratzilla/animations))
- [World Map](https://github.com/orhun/ratzilla/tree/main/examples/world_map) ([Preview](https://orhun.dev/ratzilla/world_map))
## Websites built with Ratzilla
- <https://orhun.dev/ratzilla> - The official website of Ratzilla
- <https://terminalcollective.org> - Terminal Collective community website
- <https://www.function-type.com/tusistor> - Resistor calculator
- <http://timbeck.me> - Personal website of Tim Beck
- <https://jetpham.com> - Conway's Game of Life
- <https://map.apt-swarm.orca.toys> - Map of apt-swarm p2p locations
- [TachyonFX FTL](https://junkdog.github.io/tachyonfx-ftl/) - DSL editor and previewer for TachyonFX effects
## Acknowledgements
Thanks to [Webatui] projects for the inspiration and the initial implementation of the essential parts of DOM backend.
Special thanks to [Martin Blasko] for his huge help and contributions.
Lastly, thanks to [Ratatui] for providing the core TUI components.
[trunk]: https://trunkrs.dev
[Ratatui]: https://ratatui.rs
[`DomBackend`]: https://docs.rs/ratzilla/latest/ratzilla/struct.DomBackend.html
[`CanvasBackend`]: https://docs.rs/ratzilla/latest/ratzilla/struct.CanvasBackend.html
[`Hyperlink`]: https://docs.rs/ratzilla/latest/ratzilla/widgets/struct.Hyperlink.html
[Webatui]: https://github.com/TylerBloom/webatui
[Martin Blasko]: https://github.com/MartinBspheroid
[Vercel]: https://vercel
## Contributing
Pull requests are welcome!
Consider submitting your ideas via [issues](https://github.com/orhun/ratzilla/issues/new) first and check out the [existing issues](https://github.com/orhun/ratzilla/issues).
## License
[](./LICENSE-MIT)
[](./LICENSE-APACHE)
Licensed under either of [Apache License Version 2.0](./LICENSE-APACHE) or [The MIT License](./LICENSE-MIT) at your option.
🦀 ノ( º \_ º ノ) - respect crables!
## Copyright
Copyright © 2025, [Orhun Parmaksız](mailto:
[email protected])
", Assign "at most 3 tags" to the expected json: {"id":"13526","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"