원클릭으로
routing
Use when building SPA routes, implementing route guards/loaders, configuring pp-route links, or handling route navigation in pocopine.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when building SPA routes, implementing route guards/loaders, configuring pp-route links, or handling route navigation in pocopine.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when turning a Claude Design (or any mockup/design) into a well-structured pocopine app — choosing app architecture (a store plus layout/leaf components), translating inline styles into Pine Stylekit, wiring icons and resizable regions, and verifying the result.
Use when working with the pocopine Pine icons feature — the `icon!` proc macro for compile-time Rust SVG embedding, or the `<pine-icon>` template primitive with `register_icons!` for tree-shaking-friendly template rendering.
Use when building styles with Pine Stylekit, the Pocopine-native utility-CSS compiler, or working with @theme tokens and CSS generation in Rust/WASM projects
Use when building enter/leave transitions, layout animations, stagger effects, or spring-physics motion in pocopine components
Use when implementing authentication in pocopine apps — JWT verification, credentials, OAuth providers, session management, or guards
Use when defining background jobs, enqueueing work, configuring workers, or troubleshooting job execution in pocopine apps
| name | routing |
| description | Use when building SPA routes, implementing route guards/loaders, configuring pp-route links, or handling route navigation in pocopine. |
Pocopine's SPA router maps URL patterns to #[component] types, paints matched components into a <pp-outlet>, and provides URL-aware navigation via pp-route link interception, guards, loaders, and programmatic APIs. Routes are declared centrally in App::new() and behavior is attached to components via RouteComponent trait implementation.
pp-route<pp-outlet>sRoute Declaration & Pattern Matching:
App::new().route::<Component>("/pattern") — register a route; patterns are literal (/about), parameterized (:name), or wildcard (*)/:id become component #[prop] fields via kebab→snake coercion (:post_id → post_id: T)* wildcard matches any path not matched by earlier routes (404 fallback)Route Component Interface:
pub trait RouteComponent: Component {
fn config() -> RouteConfig<Self> {
RouteConfig::new()
}
}
pub struct RouteConfig<C: Component> { … }
impl<C: Component> RouteConfig<C> {
pub fn guard(self, guard: impl RouteGuard) -> Self;
pub fn loader<F, T>(self, loader: F) -> Self
where F: Fn(LoaderContext) -> BoxFuture<Result<T, LoaderError>> + Send + Sync + 'static;
pub fn meta<T: 'static>(self, key: RouteMetaKey<T>, value: T) -> Self;
pub fn page_meta<F>(self, factory: F) -> Self where F: Fn(&RouteLocation) -> PageMeta + 'static;
}
Route Guards (Sync):
pub trait RouteGuard: 'static {
fn decide(&self, ctx: &RouteContext) -> RouteGuardDecision;
}
pub enum RouteGuardDecision {
Allow,
Redirect(RouteTarget),
Reject(RouteRejection),
}
pub enum RouteRejection {
Unauthorized,
Forbidden(&'static str),
Blocked(&'static str),
NotFound,
Server(&'static str),
Custom { reason: &'static str },
}
Route Loaders (Async):
pub enum LoaderError {
Unauthorized,
Forbidden(String),
NotFound(String),
Server(ServerError),
}
pub struct Loader<T: 'static> { … }
// Extract loader data in on_setup: `data: Loader<MyData>`
Navigation & Links:
<a href="/path" pp-route> — intercept clicks, navigate client-side (no page reload)<a href="/path" pp-route:replace> — use replace instead of pushpocopine::navigate(url: &str) — programmatic push-style navigationpocopine::push(target: impl IntoRouteTarget) -> NavigationResultpocopine::replace(target: impl IntoRouteTarget) -> NavigationResultRouteTarget::path("/path"), RouteTarget::path_with_query("/path", query) — typed navigation targetsTemplate Binding:
$route.path — current route path (reactive)$route.params.<name> — path parameter value (reactive)$route.query.<name> — query string value (reactive)Outlets & Nested Layouts:
<pp-outlet></pp-outlet> — single sentinel tag where the router mounts the matched route componentpp-outletRejection Handling & Fallback Components:
App::route_rejection_handler(handler: impl RouteRejectionHandler) — install a handler for route rejections (e.g., unauthorized → redirect to login)App::route_error_component::<C>() — custom component for unhandled rejections (fallback: generic HTML banner)App::not_found_component::<C>() — custom 404 component when no route matches and no * fallback exists1. Basic Flat SPA Routes:
// examples/spa/src/lib.rs
use pocopine::prelude::*;
#[component]
pub struct AppShell {}
#[handlers]
impl AppShell {}
#[component]
pub struct BlogPost {
#[prop]
pub id: u32,
pub body: String,
}
#[handlers]
impl BlogPost {
pub fn on_mount(&mut self) {
self.body = format!("This is post #{}.", self.id);
}
}
impl RouteComponent for BlogPost {}
#[wasm_bindgen(start)]
pub fn main() {
App::new()
.register::<AppShell>()
.route::<Home>("/")
.route::<About>("/about")
.route::<BlogPost>("/blog/:id")
.route::<NotFound>("*")
.run();
}
2. pp-route Links & $route Magic:
<!-- examples/spa/src/AppShell.poco -->
<div class="shell">
<nav class="nav">
<a href="/" pp-route>home</a>
<a href="/blog/42" pp-route>post 42</a>
<a href="/about" pp-route:replace>about</a>
</nav>
<main>
<pp-outlet></pp-outlet>
</main>
<footer>
<small>Current path: <code pp-text="$route.path"></code></small>
</footer>
</div>
3. Route Guards & Loaders:
// docs/route-guards-and-loaders.md pattern
use pocopine::prelude::*;
#[component(template = "Dashboard.poco")]
pub struct Dashboard {
pub user: AuthUser,
pub stats: DashboardStats,
}
#[handlers]
impl Dashboard {
pub fn on_setup(&mut self, data: Loader<DashboardData>) {
self.user = data.user.clone();
self.stats = data.stats.clone();
}
}
impl RouteComponent for Dashboard {
fn config() -> RouteConfig<Self> {
RouteConfig::new()
.guard(|ctx: &RouteContext| {
if AuthSession::current().is_authenticated() {
RouteGuardDecision::Allow
} else {
RouteGuardDecision::Reject(RouteRejection::Unauthorized)
}
})
.loader(|_ctx| async move {
let (user, stats) = futures::try_join!(
api::current_user(),
api::dashboard_stats(),
)?;
Ok(DashboardData { user, stats })
})
}
}
// In App::new():
App::new()
.route_rejection_handler(|ctx, rejection| match rejection {
RouteRejection::Unauthorized => {
let target = ReturnTo::current()
.append_to(RouteTarget::path("/login"), "next");
Some(RouteRejectionAction::Redirect(target))
}
_ => None,
})
.route::<Dashboard>("/dashboard")
.route::<Login>("/login")
.run();
Client Guards Are UX Only, Not Security Boundaries:
#[server] function must carry its own #[server(guard = …)] on the server sidePredicate value can be used on both client (guard) and server sides via adaptersRoute Params Are Strings & Coerced via Attributes:
/:id are captured as strings in $route.params.id#[prop] fields after type coercion (kebab-case → snake_case)One Loader Per Route:
RouteConfig::loader(...) may be called at most once per route configfutures::try_join!pp-route Link Interception Conditions:
target="_blank", same-origin, app-local URL/_pocopine/* are never intercepted (reserved server-function namespace)$route Is Read-Only in Templates:
$route.path = "/foo" has no effectpocopine::navigate() or push()/replace() from Rust insteadReserved Outlet Tag:
pp-outlet (reserved sentinel)<pp-outlet> per route component in flat routingWildcard Matching Fallback:
* pattern must be registered last (matched after all other routes)* fallback, the not_found_component (if configured) mounts insteadReturnTo Path-Only Validation:
//evil.com), backslash tricks, control characters, double-encoding attacksReturnTo::none()—a redirect without a return param, not an open-redirectRFCs:
/rfcs/rfc-003-router.md — core router design, outlets, path params, $route magic/rfcs/rfc-078-client-route-guards-and-loaders.md — guard/loader traits, rejection chains, fetch middleware, security model/rfcs/rfc-089-spa-router-parity.md — typed navigation targets, programmatic push/replace, nested layouts, meta, redirects, aliasesDocumentation:
/docs/route-guards-and-loaders.md — end-to-end guide with guard/loader patterns, rejection chains, ReturnTo, auth plugin integrationCrate:
pocopine-core router module: /crates/pocopine-core/src/router.rs — route matching, navigation, outlet management, guard/loader dispatchapp.rs: RouteComponent, RouteConfig, RouteGuard, RouteLoader, RouteRejectionHandler trait definitionsExamples:
/examples/spa/src/ — minimal flat SPA with four routes, param passing, $route.path binding/examples/hn/src/ — larger example with param-driven data loading and nested component state