topcoat README

The project's README from github.com/tokio-rs/topcoat. As one markdown file: README.md.

Topcoat

The full full-stack framework for Rust

Crates.io Docs.rs MIT licensed Build Status Discord chat

Topcoat is a modular, batteries-included Rust framework for building full-stack apps. It prioritizes simplicity and productivity. See Learn Topcoat to get started, or the Roadmap for what's coming next.

Early-stage and experimental. Expect breaking changes.

use topcoat::{
    Result,
    router::{Router, RouterBuilderDiscoverExt, page},
    view::{View, component, view},
};

#[tokio::main]
async fn main() {
    topcoat::start(Router::builder().discover().build()).await.unwrap();
}

#[page("/")]
async fn home() -> Result<impl View> {
    Ok(view! {
        <!DOCTYPE html>
        <html>
            <body>
                hello(name: "World")
            </body>
        </html>
    })
}

#[component]
async fn hello(name: &str) -> Result<impl View> {
    Ok(view! { <h1>"Hello, " (name) "!"</h1> })
}

What makes Topcoat different

Client reactivity without the boilerplate

Topcoat renders all markup on the server: components can be async and query the database directly, eliminating all the traditional boilerplate needed for a separate API layer. Interactivity does not have to cost a round-trip, though. A $(...) expression is ordinary type-checked Rust that Topcoat evaluates on the server for the initial render and also translates to JavaScript, so it re-runs instantly in the browser. No wasm bundle, no client build step:

#[component]
async fn faq(cx: &Cx) -> Result<impl View> {
    let open = signal(cx, || false);

    Ok(view! {
        // Runs entirely in the browser; no server round-trip.
        <button @click=$(|_e| open.set(!open.get()))>"What is Topcoat?"</button>
        <p :hidden=$(!open.get())>"A full-stack Rust framework."</p>
    })
}

When an update does need the server, like fresh search results, mark the component as a #[shard]. Topcoat re-renders it on the server whenever one of its $(...) arguments changes and swaps the new HTML in place:

#[component]
async fn search(cx: &Cx) -> Result<impl View> {
    let query = signal(cx, String::new);

    Ok(view! {
        <input @input=$(|e: Event| query.set(e.target.value))>

        // Updates as the user types.
        search_results(query: $(query.get()))
    })
}

#[shard]
async fn search_results(cx: &Cx, query: String) -> Result<impl View> {
    Ok(view! {
        <ul>
            // Your own server-side code, like a database query:
            for product in search_products(cx, &query).await? {
                <li>(product.name)</li>
            }
        </ul>
    })
}

Powerful, unsurprising HTML templates

The view! macro stays true to HTML and Rust. Use familiar Rust control flow as part of your templates:

view! {
    <nav>
        for item in nav_items {
            <a
                href=(item.url)
                if item.url == current_path {
                    aria-current="page"
                    class="active"
                }
            >
                (item.label)
            </a>
        }
    </nav>
}

Use the topcoat fmt CLI command to automatically format view! snippets (and other macros) across your codebase.

Module-based routing

Topcoat can optionally infer your route tree from your app's module structure (without a build step):

src/
|-- app.rs              -> /            (and the root <html> layout)
`-- app/
    |-- about.rs        -> /about
    |-- _marketing.rs                  (layout, no URL segment)
    |-- _marketing/
    |   `-- pricing.rs  -> /pricing
    |-- posts.rs        -> /posts
    |-- posts/
    |   `-- id.rs       -> /posts/{post_id}
    `-- api/
        `-- health.rs   -> GET /api/health

Premade components you can edit

Topcoat UI is a component library based on Tailwind inspired by shadcn/ui. Components are copied into your project via the topcoat ui CLI command, meaning you can freely change their design and functionality to fit your use case:

#[component]
async fn delete_card() -> Result<impl View> {
    Ok(view! {
        card(
            card_header(
                card_title("Delete workspace")
                card_description(
                    "This permanently removes the workspace and all of its data."
                )
            )
            card_footer(
                attrs: attributes! { class="justify-end" },
                button(variant: ButtonVariant::Ghost, "Cancel")
                button(variant: ButtonVariant::Destructive, "Delete workspace")
            )
        )
    })
}

Asset bundling

The bundler scans your compiled binary for asset! calls, copies (or even downloads) every file into a local asset directory, and allows Topcoat to serve them efficiently with aggressive browser caching.

const FERRIS: Asset = asset!("./ferris.png");

view! { <img src=(FERRIS)> }

Topcoat also ships with utilities for web fonts and icons, as well as easy integrations for Fontsource (Google Fonts) and Iconify.

Built-in Tailwind support

Enable the tailwind feature to integrate Tailwind into your project:

view! { <link rel="stylesheet" href=(topcoat::tailwind::stylesheet!())> }

Learn Topcoat

Start here

Rendering

Routing

Working with requests

Asset system

Client reactivity

Miscellaneous

Third-party integrations

Roadmap

Planned features we'd like to bring to Topcoat. Have an idea? Open an issue.

topcoat (tokio-rs/topcoat)

Searching Database…

6.1K ·GitHub
What is this?Topcoat is a batteries-included, modular Rust framework for building full-stack web apps, where components render on the server and can query the database directly.
How is this different from typical Rust web frameworks?Topcoat renders all markup on the server and lets components be async and query the database directly, removing the need for a separate API layer; its $(...) expressions are plain Rust that also run in the browser, with no wasm bundle or client build step.
How do I get a Topcoat project running?You follow the Getting started guide to create a new project, install the Topcoat CLI, and run the dev server.
Do I need Node.js or a separate JS build step for the reactive parts?No. The $(...) expressions are ordinary Rust that Topcoat evaluates on the server and also translates to JavaScript itself, so there is no wasm bundle and no client build step; Tailwind can also be enabled through a built-in feature flag.
Is Topcoat ready for production use?No, the project explicitly describes itself as early-stage and experimental, and breaking changes should be expected.
What are 'shards' in Topcoat?Shards are components marked with #[shard] that Topcoat re-renders on the server whenever one of their $(...) arguments changes, swapping the new HTML into the page in place.
What is Topcoat UI?Topcoat UI is a Tailwind-based component library inspired by shadcn/ui; components are copied into your project via the topcoat ui CLI command so you can freely edit their design and behavior.
Can routes be set up without manual configuration?Yes, Topcoat can optionally infer your entire route tree directly from your app's module structure, with no build step required.
Does Topcoat handle authentication yet?Not fully; it already ships session-based auth with login/logout, sliding expiration, and token rotation, but full authentication support and validations are still on the roadmap.
Creator
tokio-rs
Size
6.9 MB
Stars
6.1K
Age
6mo
Commits · 90 Days