> leptos
You are an expert in Leptos, the full-stack Rust web framework that combines server-side rendering with client-side interactivity via WebAssembly. You help developers build reactive web applications using Leptos's fine-grained signal system, server functions, islands architecture, and compile-time optimizations — getting React-like DX with Rust's performance and type safety.
curl "https://skillshub.wtf/TerminalSkills/skills/leptos?format=md"Leptos — Full-Stack Rust Web Framework
You are an expert in Leptos, the full-stack Rust web framework that combines server-side rendering with client-side interactivity via WebAssembly. You help developers build reactive web applications using Leptos's fine-grained signal system, server functions, islands architecture, and compile-time optimizations — getting React-like DX with Rust's performance and type safety.
Core Capabilities
Reactive Components
use leptos::*;
#[component]
fn Counter() -> impl IntoView {
let (count, set_count) = create_signal(0);
view! {
<div class="counter">
<button on:click=move |_| set_count.update(|n| *n -= 1)>"-"</button>
<span>{count}</span>
<button on:click=move |_| set_count.update(|n| *n += 1)>"+"</button>
</div>
}
}
#[component]
fn TodoApp() -> impl IntoView {
let (todos, set_todos) = create_signal(Vec::<String>::new());
let (input, set_input) = create_signal(String::new());
let add_todo = move |_| {
let value = input.get();
if !value.is_empty() {
set_todos.update(|t| t.push(value.clone()));
set_input.set(String::new());
}
};
view! {
<div>
<input
prop:value=input
on:input=move |ev| set_input.set(event_target_value(&ev))
on:keydown=move |ev| {
if ev.key() == "Enter" { add_todo(ev) }
}
/>
<button on:click=add_todo>"Add"</button>
<ul>
<For
each=move || todos.get().into_iter().enumerate()
key=|(i, _)| *i
children=move |(_, todo)| view! { <li>{todo}</li> }
/>
</ul>
</div>
}
}
Server Functions
// Server functions run on the server, callable from client
#[server(GetPosts, "/api")]
pub async fn get_posts(page: u32) -> Result<Vec<Post>, ServerFnError> {
let db = use_context::<PgPool>().unwrap();
let posts = sqlx::query_as!(
Post,
"SELECT * FROM posts ORDER BY created_at DESC LIMIT 20 OFFSET $1",
((page - 1) * 20) as i64,
)
.fetch_all(&db)
.await?;
Ok(posts)
}
#[server(CreatePost, "/api")]
pub async fn create_post(title: String, body: String) -> Result<Post, ServerFnError> {
let db = use_context::<PgPool>().unwrap();
let user = use_context::<AuthUser>().ok_or(ServerFnError::new("Unauthorized"))?;
let post = sqlx::query_as!(
Post,
"INSERT INTO posts (title, body, author_id) VALUES ($1, $2, $3) RETURNING *",
title, body, user.id,
)
.fetch_one(&db)
.await?;
Ok(post)
}
// Use in component — works on both server (SSR) and client (WASM)
#[component]
fn PostList() -> impl IntoView {
let posts = create_resource(|| (), |_| get_posts(1));
view! {
<Suspense fallback=move || view! { <p>"Loading..."</p> }>
{move || posts.get().map(|result| match result {
Ok(posts) => view! {
<ul>
{posts.into_iter().map(|p| view! {
<li><a href=format!("/posts/{}", p.id)>{&p.title}</a></li>
}).collect_view()}
</ul>
}.into_view(),
Err(e) => view! { <p class="error">{e.to_string()}</p> }.into_view(),
})}
</Suspense>
}
}
SSR + Hydration
// main.rs — Full-stack setup with Actix
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let db = PgPool::connect(&env::var("DATABASE_URL").unwrap()).await.unwrap();
HttpServer::new(move || {
let leptos_options = get_configuration(None).await.unwrap().leptos_options;
App::new()
.app_data(web::Data::new(db.clone()))
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
.leptos_routes(
leptos_options.clone(),
generate_route_list(App),
App,
)
.service(Files::new("/", &leptos_options.site_root))
})
.bind("0.0.0.0:3000")?
.run()
.await
}
Installation
cargo install cargo-leptos
cargo leptos new --git leptos-rs/start
cd my-app
cargo leptos watch # Dev server with hot reload
cargo leptos build --release # Production build
Best Practices
- Fine-grained signals — Leptos tracks signal dependencies at the expression level; only the exact DOM nodes that depend on a signal update
- Server functions — Use
#[server]for database queries, auth, file I/O; they compile to API endpoints automatically - Suspense for async — Wrap async data loading in
<Suspense>; shows fallback during loading, streams content with SSR - Islands for performance — Use islands architecture: static HTML with interactive WASM islands; minimal JS shipped
- Type-safe routing — Use
leptos_routerfor compile-time checked routes; no runtime 404s from typos - Resource for data fetching —
create_resourcehandles loading states, caching, and refetching automatically - Context for DI — Use
provide_context/use_contextfor database pools, auth state, config - WASM size — Leptos produces small WASM bundles (~200KB gzipped for a full app); fine-grained reactivity means less code shipped
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.