KS
Killer-Skills

lightfriend-add-frontend-page — how to use lightfriend-add-frontend-page how to use lightfriend-add-frontend-page, lightfriend-add-frontend-page setup guide, Yew WebAssembly frontend development, adding new pages to Lightfriend, lightfriend-add-frontend-page alternative, lightfriend-add-frontend-page vs other AI assistants

v1.0.0
GitHub

About this Skill

Perfect for WebAssembly-focused Agents needing to extend the Lightfriend Yew frontend with custom pages. lightfriend-add-frontend-page is a skill that assists in adding new frontend pages to the Lightfriend Yew WebAssembly application, including page components, routes, and navigation links.

Features

Creates page components in frontend/src/pages/
Adds route enum variants in main.rs
Handles routes in switch functions
Supports navigation link creation
Utilizes Yew and gloo_net::http::Request for frontend development

# Core Topics

ahtavarasmus ahtavarasmus
[65]
[7]
Updated: 3/3/2026

Quality Score

Top 5%
41
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add ahtavarasmus/lightfriend

Agent Capability Analysis

The lightfriend-add-frontend-page MCP Server by ahtavarasmus is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use lightfriend-add-frontend-page, lightfriend-add-frontend-page setup guide, Yew WebAssembly frontend development.

Ideal Agent Persona

Perfect for WebAssembly-focused Agents needing to extend the Lightfriend Yew frontend with custom pages.

Core Value

Empowers agents to create new page components, route enums, and navigation links for the Lightfriend Yew WebAssembly frontend, utilizing libraries like Yew and gloo_net.

Capabilities Granted for lightfriend-add-frontend-page MCP Server

Generating new page components in frontend/src/pages/
Defining route enum variants in main.rs for custom navigation
Creating navigation links for seamless user experience

! Prerequisites & Limits

  • Requires knowledge of Rust programming language
  • Specific to Lightfriend Yew WebAssembly frontend
  • Needs access to frontend/src/pages/ and main.rs files
Project
SKILL.md
11.0 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Adding a New Frontend Page

This skill guides you through adding a new page to the Lightfriend Yew WebAssembly frontend.

Overview

A complete page includes:

  • Page component in frontend/src/pages/
  • Route enum variant in main.rs
  • Route handler in switch function
  • Navigation link (if applicable)

Step-by-Step Process

1. Create Page Component

Create frontend/src/pages/{page_name}.rs:

rust
1use yew::prelude::*; 2use gloo_net::http::Request; 3use crate::config; 4 5#[function_component(PageName)] 6pub fn page_name() -> Html { 7 // State management 8 let data = use_state(|| None::<SomeData>); 9 let loading = use_state(|| true); 10 let error = use_state(|| None::<String>); 11 12 // Load data on mount 13 { 14 let data = data.clone(); 15 let loading = loading.clone(); 16 let error = error.clone(); 17 18 use_effect_with((), move |_| { 19 wasm_bindgen_futures::spawn_local(async move { 20 match fetch_data().await { 21 Ok(result) => { 22 data.set(Some(result)); 23 loading.set(false); 24 } 25 Err(e) => { 26 error.set(Some(e.to_string())); 27 loading.set(false); 28 } 29 } 30 }); 31 }); 32 } 33 34 html! { 35 <div class="page-container"> 36 <h1>{"Page Title"}</h1> 37 38 if *loading { 39 <p>{"Loading..."}</p> 40 } else if let Some(err) = (*error).clone() { 41 <p class="error">{err}</p> 42 } else if let Some(content) = (*data).clone() { 43 // Render content 44 <div> 45 {format!("Content: {:?}", content)} 46 </div> 47 } 48 </div> 49 } 50} 51 52// Helper functions 53async fn fetch_data() -> Result<SomeData, Box<dyn std::error::Error>> { 54 let token = /* get from context or local storage */; 55 let backend_url = config::get_backend_url(); 56 57 let response = Request::get(&format!("{}/api/endpoint", backend_url)) 58 .header("Authorization", &format!("Bearer {}", token)) 59 .send() 60 .await? 61 .json::<SomeData>() 62 .await?; 63 64 Ok(response) 65} 66 67#[derive(Clone, serde::Deserialize, serde::Serialize)] 68struct SomeData { 69 // Define your data structure 70}

2. Add Module Declaration

CRITICAL: This codebase does NOT use mod.rs files!

Instead, add the module declaration to the inline mod pages { } block in frontend/src/main.rs:

rust
1mod pages { 2 pub mod home; 3 pub mod landing; 4 pub mod {page_name}; // Add your new page here 5 // ... other pages 6}

NEVER create a mod.rs file - this is a common mistake. Lightfriend uses named module files (e.g., home.rs, landing.rs) and declares them in the inline module block in main.rs.

3. Add Route Variant

In frontend/src/main.rs, add a route variant to the Route enum:

rust
1#[derive(Clone, Routable, PartialEq)] 2pub enum Route { 3 #[at("/")] 4 Home, 5 #[at("/page-name")] 6 PageName, 7 // ... other routes 8 #[not_found] 9 #[at("/404")] 10 NotFound, 11}

4. Add Route Handler

In the switch() function in frontend/src/main.rs, add:

rust
1fn switch(route: Route) -> Html { 2 match route { 3 Route::Home => html! { <Home /> }, 4 Route::PageName => html! { <PageName /> }, 5 // ... other routes 6 Route::NotFound => html! { <h1>{"404 - Page Not Found"}</h1> }, 7 } 8}

5. Add Navigation Link (Optional)

If the page should appear in navigation, add to the Nav component in frontend/src/main.rs:

rust
1#[function_component(Nav)] 2fn nav() -> Html { 3 html! { 4 <nav> 5 <Link<Route> to={Route::Home}>{"Home"}</Link<Route>> 6 <Link<Route> to={Route::PageName}>{"Page Name"}</Link<Route>> 7 // ... other links 8 </nav> 9 } 10}

6. Test the Page

bash
1cd frontend && trunk serve

Navigate to http://localhost:8080/page-name

Common Patterns

Protected Routes (Require Auth)

rust
1use yew_hooks::use_local_storage; 2 3#[function_component(ProtectedPage)] 4pub fn protected_page() -> Html { 5 let token = use_local_storage::<String>("token".to_string()); 6 7 if token.is_none() { 8 // Redirect to login 9 let navigator = use_navigator().unwrap(); 10 navigator.push(&Route::Login); 11 return html! {}; 12 } 13 14 html! { 15 <div>{"Protected content"}</div> 16 } 17}

Page with Form

rust
1use web_sys::HtmlInputElement; 2 3#[function_component(FormPage)] 4pub fn form_page() -> Html { 5 let name_ref = use_node_ref(); 6 let email_ref = use_node_ref(); 7 let submitting = use_state(|| false); 8 9 let on_submit = { 10 let name_ref = name_ref.clone(); 11 let email_ref = email_ref.clone(); 12 let submitting = submitting.clone(); 13 14 Callback::from(move |e: SubmitEvent| { 15 e.prevent_default(); 16 let submitting = submitting.clone(); 17 18 let name = name_ref.cast::<HtmlInputElement>() 19 .unwrap() 20 .value(); 21 let email = email_ref.cast::<HtmlInputElement>() 22 .unwrap() 23 .value(); 24 25 submitting.set(true); 26 27 wasm_bindgen_futures::spawn_local(async move { 28 match submit_form(name, email).await { 29 Ok(_) => { 30 // Handle success 31 } 32 Err(e) => { 33 // Handle error 34 } 35 } 36 submitting.set(false); 37 }); 38 }) 39 }; 40 41 html! { 42 <form onsubmit={on_submit}> 43 <input 44 ref={name_ref} 45 type="text" 46 placeholder="Name" 47 required=true 48 /> 49 <input 50 ref={email_ref} 51 type="email" 52 placeholder="Email" 53 required=true 54 /> 55 <button type="submit" disabled={*submitting}> 56 if *submitting { 57 {"Submitting..."} 58 } else { 59 {"Submit"} 60 } 61 </button> 62 </form> 63 } 64} 65 66async fn submit_form(name: String, email: String) -> Result<(), Box<dyn std::error::Error>> { 67 let token = /* get from context */; 68 69 Request::post(&format!("{}/api/submit", config::get_backend_url())) 70 .header("Authorization", &format!("Bearer {}", token)) 71 .json(&serde_json::json!({ 72 "name": name, 73 "email": email, 74 }))? 75 .send() 76 .await?; 77 78 Ok(()) 79}

Page with Context

rust
1use yew::prelude::*; 2 3#[derive(Clone, PartialEq)] 4pub struct UserContext { 5 pub user_id: i32, 6 pub email: String, 7} 8 9#[function_component(ContextPage)] 10pub fn context_page() -> Html { 11 let user_ctx = use_context::<UserContext>() 12 .expect("UserContext not found"); 13 14 html! { 15 <div> 16 <p>{format!("User ID: {}", user_ctx.user_id)}</p> 17 <p>{format!("Email: {}", user_ctx.email)}</p> 18 </div> 19 } 20}

Page with Route Parameters

rust
1#[derive(Clone, Routable, PartialEq)] 2pub enum Route { 3 #[at("/users/:id")] 4 UserDetail { id: i32 }, 5} 6 7#[derive(Properties, PartialEq)] 8pub struct UserDetailProps { 9 pub id: i32, 10} 11 12#[function_component(UserDetail)] 13pub fn user_detail(props: &UserDetailProps) -> Html { 14 let user_data = use_state(|| None::<User>); 15 16 { 17 let user_data = user_data.clone(); 18 let user_id = props.id; 19 20 use_effect_with(user_id, move |_| { 21 wasm_bindgen_futures::spawn_local(async move { 22 if let Ok(user) = fetch_user(user_id).await { 23 user_data.set(Some(user)); 24 } 25 }); 26 }); 27 } 28 29 html! { 30 <div> 31 if let Some(user) = (*user_data).clone() { 32 <h1>{user.name}</h1> 33 } 34 </div> 35 } 36} 37 38// In switch function: 39fn switch(route: Route) -> Html { 40 match route { 41 Route::UserDetail { id } => html! { <UserDetail id={id} /> }, 42 // ... 43 } 44}

Page with Multiple API Calls

rust
1#[function_component(DashboardPage)] 2pub fn dashboard_page() -> Html { 3 let stats = use_state(|| None::<Stats>); 4 let activity = use_state(|| None::<Vec<Activity>>); 5 let loading = use_state(|| true); 6 7 { 8 let stats = stats.clone(); 9 let activity = activity.clone(); 10 let loading = loading.clone(); 11 12 use_effect_with((), move |_| { 13 wasm_bindgen_futures::spawn_local(async move { 14 // Fetch multiple endpoints in parallel 15 let (stats_result, activity_result) = tokio::join!( 16 fetch_stats(), 17 fetch_activity() 18 ); 19 20 if let Ok(s) = stats_result { 21 stats.set(Some(s)); 22 } 23 if let Ok(a) = activity_result { 24 activity.set(Some(a)); 25 } 26 27 loading.set(false); 28 }); 29 }); 30 } 31 32 html! { 33 <div> 34 if *loading { 35 <p>{"Loading dashboard..."}</p> 36 } else { 37 <div> 38 {render_stats(&stats)} 39 {render_activity(&activity)} 40 </div> 41 } 42 </div> 43 } 44}

Styling

Lightfriend uses CSS style blocks within the html! macro, NOT inline Tailwind classes.

Common pattern:

rust
1html! { 2 <div class="page-container"> 3 <h1 class="page-title">{"Title"}</h1> 4 <div class="content-grid"> 5 <div class="card"> 6 {"Card content"} 7 </div> 8 </div> 9 10 <style> 11 {r#" 12 .page-container { 13 max-width: 1200px; 14 margin: 0 auto; 15 padding: 2rem; 16 } 17 .page-title { 18 font-size: 2rem; 19 font-weight: bold; 20 margin-bottom: 1rem; 21 } 22 .content-grid { 23 display: grid; 24 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 25 gap: 1rem; 26 } 27 .card { 28 background: white; 29 border-radius: 8px; 30 box-shadow: 0 2px 4px rgba(0,0,0,0.1); 31 padding: 1rem; 32 } 33 "#} 34 </style> 35 </div> 36}

Testing Checklist

  • Page renders without errors
  • Route works in browser
  • Navigation link works (if added)
  • API calls succeed
  • Loading states display correctly
  • Error states display correctly
  • Authentication checks work (if protected)
  • Mobile responsive (if applicable)

File Reference

  • Page components: frontend/src/pages/{page}.rs
  • Routes: frontend/src/main.rs (Route enum + switch function)
  • Navigation: frontend/src/main.rs (Nav component)
  • Shared components: frontend/src/components/
  • Backend config: frontend/src/config.rs

Related Skills

Looking for an alternative to lightfriend-add-frontend-page or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication