Install
$ agentstack add skill-bwbioinfo-skill-dioxus-dioxus-fullstack-dev Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Destructive filesystem operation.
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Dioxus Fullstack Development
This skill helps you build comprehensive fullstack web applications using Dioxus with server-side rendering, server functions, and seamless client-server integration.
When to use this skill
- Building fullstack web applications with SSR (Server-Side Rendering)
- Implementing server functions for backend logic
- Setting up client-server communication and data fetching
- Configuring hydration for optimal performance
- Building APIs with authentication and database integration
- Deploying fullstack Dioxus applications
Fullstack Setup
1. Project Configuration
Cargo.toml for Fullstack:
[package]
name = "my-fullstack-app"
version = "0.1.0"
edition = "2021"
[dependencies]
dioxus = { version = "0.7", features = ["fullstack"] }
dioxus-router = "0.7"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] }
uuid = { version = "1.0", features = ["v4"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"
tracing = "0.1"
tracing-subscriber = "0.3"
# Client-specific dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
axum = "0.7"
tower = "0.4"
tower-http = { version = "0.4", features = ["fs", "cors"] }
# WASM-specific dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
web-sys = "0.3"
Dioxus.toml Configuration:
[application]
name = "my-fullstack-app"
default_platform = "fullstack"
[web.app]
title = "My Fullstack App"
base_path = "."
[web.watcher]
watch_path = ["src", "assets"]
reload_html = true
[web.resource]
dev = true
style = ["./assets/main.css"]
[bundle]
identifier = "com.example.myapp"
publisher = "Example Publisher"
2. Application Structure
src/main.rs (Server Entry Point):
#![allow(non_snake_case)]
use dioxus::prelude::*;
use dioxus_fullstack::prelude::*;
mod app;
mod components;
mod server;
mod database;
mod auth;
#[tokio::main]
async fn main() {
// Initialize tracing
tracing_subscriber::init();
// Initialize database
let db_pool = database::init().await.expect("Failed to initialize database");
// Configure the server
let config = ServeConfig::builder()
.assets_path("assets")
.incremental(
IncrementalRendererConfig::default()
.static_dir("./static")
)
.build();
// Launch the fullstack app
LaunchBuilder::new()
.with_cfg(config)
.with_context(db_pool)
.launch(app::App)
.await
.unwrap();
}
src/app.rs (Root Component):
#![allow(non_snake_case)]
use dioxus::prelude::*;
use dioxus_router::prelude::*;
use crate::components::*;
#[derive(Clone, Routable, Debug, PartialEq)]
enum Route {
#[route("/")]
Home {},
#[route("/login")]
Login {},
#[route("/register")]
Register {},
#[layout(AuthLayout)]
#[route("/dashboard")]
Dashboard {},
#[route("/profile")]
Profile {},
#[route("/posts")]
Posts {},
#[route("/posts/new")]
CreatePost {},
#[route("/posts/:id")]
ViewPost { id: i32 },
#[end_layout]
#[route("/:..route")]
PageNotFound { route: Vec },
}
#[component]
pub fn App() -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("./assets/main.css") }
Router:: {}
}
}
#[component]
fn Home() -> Element {
let mut posts = use_resource(|| get_recent_posts());
rsx! {
div {
class: "home",
Header {}
main {
class: "container",
h1 { "Welcome to My Fullstack App" }
section {
class: "recent-posts",
h2 { "Recent Posts" }
match posts() {
Some(Ok(posts_data)) => rsx! {
div {
class: "posts-grid",
for post in posts_data {
PostCard {
key: "{post.id}",
post: post.clone()
}
}
}
},
Some(Err(_)) => rsx! {
div { class: "error", "Failed to load posts" }
},
None => rsx! {
div { class: "loading", "Loading posts..." }
}
}
}
}
Footer {}
}
}
}
3. Server Functions
Define Server Functions:
use dioxus::prelude::*;
use dioxus_fullstack::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Post {
pub id: i32,
pub title: String,
pub content: String,
pub author_id: i32,
pub created_at: chrono::DateTime,
pub updated_at: chrono::DateTime,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CreatePostRequest {
pub title: String,
pub content: String,
}
#[server(GetRecentPosts)]
pub async fn get_recent_posts() -> Result, ServerFnError> {
use crate::database::*;
let db = get_db_pool()?;
let posts = sqlx::query_as!(
Post,
"SELECT id, title, content, author_id, created_at, updated_at
FROM posts
ORDER BY created_at DESC
LIMIT 10"
)
.fetch_all(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(posts)
}
#[server(GetPost)]
pub async fn get_post(id: i32) -> Result, ServerFnError> {
use crate::database::*;
let db = get_db_pool()?;
let post = sqlx::query_as!(
Post,
"SELECT id, title, content, author_id, created_at, updated_at
FROM posts
WHERE id = $1",
id
)
.fetch_optional(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(post)
}
#[server(CreatePost)]
pub async fn create_post(request: CreatePostRequest) -> Result {
use crate::database::*;
use crate::auth::*;
let db = get_db_pool()?;
let user_id = get_current_user_id()?;
let post = sqlx::query_as!(
Post,
"INSERT INTO posts (title, content, author_id, created_at, updated_at)
VALUES ($1, $2, $3, NOW(), NOW())
RETURNING id, title, content, author_id, created_at, updated_at",
request.title,
request.content,
user_id
)
.fetch_one(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(post)
}
#[server(UpdatePost)]
pub async fn update_post(id: i32, request: CreatePostRequest) -> Result {
use crate::database::*;
use crate::auth::*;
let db = get_db_pool()?;
let user_id = get_current_user_id()?;
let post = sqlx::query_as!(
Post,
"UPDATE posts
SET title = $1, content = $2, updated_at = NOW()
WHERE id = $3 AND author_id = $4
RETURNING id, title, content, author_id, created_at, updated_at",
request.title,
request.content,
id,
user_id
)
.fetch_one(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Ok(post)
}
#[server(DeletePost)]
pub async fn delete_post(id: i32) -> Result {
use crate::database::*;
use crate::auth::*;
let db = get_db_pool()?;
let user_id = get_current_user_id()?;
let rows_affected = sqlx::query!(
"DELETE FROM posts WHERE id = $1 AND author_id = $2",
id,
user_id
)
.execute(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
.rows_affected();
if rows_affected == 0 {
return Err(ServerFnError::ServerError("Post not found or unauthorized".to_string()));
}
Ok(())
}
4. Authentication System
src/auth.rs:
use dioxus::prelude::*;
use dioxus_fullstack::prelude::*;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct User {
pub id: i32,
pub email: String,
pub name: String,
pub created_at: chrono::DateTime,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LoginRequest {
pub email: String,
pub password: String,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct RegisterRequest {
pub email: String,
pub name: String,
pub password: String,
}
#[server(Login)]
pub async fn login(request: LoginRequest) -> Result {
use crate::database::*;
use argon2::{Argon2, PasswordHash, PasswordVerifier};
let db = get_db_pool()?;
// Get user and password hash from database
let user_record = sqlx::query!(
"SELECT id, email, name, password_hash, created_at FROM users WHERE email = $1",
request.email
)
.fetch_optional(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
let user_record = user_record.ok_or_else(|| {
ServerFnError::ServerError("Invalid credentials".to_string())
})?;
// Verify password
let parsed_hash = PasswordHash::new(&user_record.password_hash)
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
Argon2::default()
.verify_password(request.password.as_bytes(), &parsed_hash)
.map_err(|_| ServerFnError::ServerError("Invalid credentials".to_string()))?;
// Create session
let session_token = Uuid::new_v4().to_string();
sqlx::query!(
"INSERT INTO sessions (user_id, token, expires_at)
VALUES ($1, $2, NOW() + INTERVAL '30 days')",
user_record.id,
session_token
)
.execute(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
// Set session cookie
let mut response = Response::new("".to_string());
response.headers_mut().insert(
"Set-Cookie",
format!("session={}; Path=/; HttpOnly; SameSite=Strict; Max-Age=2592000", session_token)
.parse()
.unwrap(),
);
let user = User {
id: user_record.id,
email: user_record.email,
name: user_record.name,
created_at: user_record.created_at,
};
Ok(user)
}
#[server(Register)]
pub async fn register(request: RegisterRequest) -> Result {
use crate::database::*;
use argon2::{Argon2, PasswordHasher, password_hash::{SaltString, rand_core::OsRng}};
let db = get_db_pool()?;
// Check if user already exists
let existing_user = sqlx::query!(
"SELECT id FROM users WHERE email = $1",
request.email
)
.fetch_optional(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
if existing_user.is_some() {
return Err(ServerFnError::ServerError("User already exists".to_string()));
}
// Hash password
let salt = SaltString::generate(&mut OsRng);
let password_hash = Argon2::default()
.hash_password(request.password.as_bytes(), &salt)
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
.to_string();
// Create user
let user_record = sqlx::query!(
"INSERT INTO users (email, name, password_hash, created_at)
VALUES ($1, $2, $3, NOW())
RETURNING id, email, name, created_at",
request.email,
request.name,
password_hash
)
.fetch_one(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
let user = User {
id: user_record.id,
email: user_record.email,
name: user_record.name,
created_at: user_record.created_at,
};
Ok(user)
}
#[server(GetCurrentUser)]
pub async fn get_current_user() -> Result, ServerFnError> {
use crate::database::*;
let user_id = match get_current_user_id() {
Ok(id) => id,
Err(_) => return Ok(None),
};
let db = get_db_pool()?;
let user_record = sqlx::query!(
"SELECT id, email, name, created_at FROM users WHERE id = $1",
user_id
)
.fetch_optional(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
let user = user_record.map(|record| User {
id: record.id,
email: record.email,
name: record.name,
created_at: record.created_at,
});
Ok(user)
}
#[server(Logout)]
pub async fn logout() -> Result {
use crate::database::*;
let db = get_db_pool()?;
if let Ok(session_token) = get_session_token() {
sqlx::query!(
"DELETE FROM sessions WHERE token = $1",
session_token
)
.execute(&db)
.await
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
}
// Clear session cookie
let mut response = Response::new("".to_string());
response.headers_mut().insert(
"Set-Cookie",
"session=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0"
.parse()
.unwrap(),
);
Ok(())
}
// Helper functions for server context
pub fn get_current_user_id() -> Result {
// Implementation would extract user ID from session
todo!("Implement session-based user ID extraction")
}
pub fn get_session_token() -> Result {
// Implementation would extract session token from cookies
todo!("Implement session token extraction")
}
5. Client Components with Server Integration
src/components/login_form.rs:
use dioxus::prelude::*;
use crate::auth::*;
#[component]
pub fn LoginForm() -> Element {
let mut email = use_signal(|| String::new());
let mut password = use_signal(|| String::new());
let mut is_loading = use_signal(|| false);
let mut error_message = use_signal(|| None::);
let navigator = use_navigator();
let handle_submit = move |_| {
is_loading.set(true);
error_message.set(None);
let email_val = email();
let password_val = password();
spawn(async move {
match login(LoginRequest {
email: email_val,
password: password_val,
}).await {
Ok(_user) => {
navigator.push(Route::Dashboard {});
}
Err(e) => {
error_message.set(Some(e.to_string()));
is_loading.set(false);
}
}
});
};
rsx! {
div {
class: "login-form-container",
form {
class: "login-form",
onsubmit: handle_submit,
h2 { "Login" }
if let Some(error) = error_message() {
div {
class: "error-message",
"{error}"
}
}
div {
class: "form-group",
label { "Email:" }
input {
r#type: "email",
required: true,
value: "{email}",
oninput: move |evt| email.set(evt.value()),
disabled: is_loading()
}
}
div {
class: "form-group",
label { "Password:" }
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [bwbioinfo](https://github.com/bwbioinfo)
- **Source:** [bwbioinfo/skill_dioxus](https://github.com/bwbioinfo/skill_dioxus)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.