{% extends "docs_base.html" %}
{% block title %}Frontend Development - Ruskit Documentation{% endblock %}
{% block description %}Learn about frontend development with Ruskit using React, TypeScript, and Tailwind CSS{% endblock %}
{% block content %}
<div class="prose prose-slate max-w-none">
<h1>Frontend Development</h1>
<p class="lead">
Ruskit provides a modern frontend development experience using React, TypeScript, and Tailwind CSS,
with full type safety between your Rust backend and React frontend.
</p>
<h2 id="react-with-inertia">React with Inertia.js</h2>
<p>
Ruskit uses <a href="https://inertiajs.com/" class="link">Inertia.js</a>
to connect your React frontend with your Rust backend, providing a seamless single-page application
experience without building an API.
</p>
<h3 id="type-safe-props">Type-Safe Props</h3>
<p>
Ruskit automatically generates TypeScript types from your Rust DTOs using <code>ts-rs</code>.
This ensures complete type safety between your backend and frontend.
</p>
<div class="code-block">
<h4>1. Define your DTO in Rust:</h4>
<pre class="line-numbers"><code class="language-rust">use serde::Serialize;
use ts_export_derive::auto_ts_export;
#[auto_ts_export]
pub struct AboutPageProps {
pub title: String,
pub description: String,
pub tech_stack: Vec<String>,
pub why_choose_us: Vec<String>,
}</code></pre>
</div>
<div class="code-block">
<h4>2. Generated TypeScript types:</h4>
<pre class="line-numbers"><code class="language-typescript">export interface AboutPageProps {
title: string;
description: string;
tech_stack: string[];
why_choose_us: string[];
}</code></pre>
</div>
<h3 id="creating-pages">Creating Pages</h3>
<p>
Ruskit provides CLI commands to quickly scaffold Inertia pages and props:
</p>
<div class="code-block">
<h4>Generate a complete Inertia page:</h4>
<pre class="line-numbers"><code class="language-shell-session">$ cargo kit inertia:page Dashboard</code></pre>
<p>This will create:</p>
<ul>
<li>Props type in <code>src/app/dtos/dashboard.rs</code></li>
<li>Controller in <code>src/app/controllers/dashboard_controller.rs</code></li>
<li>React component in <code>resources/js/pages/Dashboard.tsx</code></li>
</ul>
</div>
<div class="code-block">
<h4>Generate just the props type:</h4>
<pre class="line-numbers"><code class="language-shell-session">$ cargo kit inertia:prop Settings</code></pre>
<p>Useful when:</p>
<ul>
<li>Adding props to an existing page</li>
<li>Creating shared props used by multiple components</li>
<li>Defining the contract before implementing the UI</li>
</ul>
</div>
<div class="code-block">
<h4>1. Generated Props Type:</h4>
<pre class="line-numbers"><code class="language-rust">use serde::Serialize;
use ts_export_derive::auto_ts_export;
#[auto_ts_export]
pub struct DashboardProps {
pub title: String,
// TODO: Add your page props here
}</code></pre>
</div>
<div class="code-block">
<h4>2. Generated Controller:</h4>
<pre class="line-numbers"><code class="language-rust">use axum::response::IntoResponse;
use axum_inertia::Inertia;
use crate::app::dtos::dashboard::DashboardProps;
pub struct DashboardController;
impl DashboardController {
pub async fn show(inertia: Inertia) -> impl IntoResponse {
inertia.render("Dashboard", DashboardProps {
title: String::from("Dashboard"),
})
}
}</code></pre>
</div>
<div class="code-block">
<h4>3. Generated React Component:</h4>
<pre class="line-numbers"><code class="language-typescript">import React from 'react';
import { Head } from '@inertiajs/react';
import type { DashboardProps } from '../types/generated';
interface Props extends DashboardProps {}
export default function Dashboard({ title }: Props) {
return (
<>
<Head title={title} />
<div className="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h1 className="text-4xl font-bold text-gray-900 sm:text-5xl">
{title}
</h1>
{/* Add your page content here */}
</div>
</div>
</>
);
}</code></pre>
</div>
<div class="info-block">
<h4>Next Steps After Generation</h4>
<ol>
<li>Add your route in <code>src/web.rs</code>:
<pre class="line-numbers"><code class="language-rust">.route("/dashboard", get(DashboardController::show))</code></pre>
</li>
<li>Add your props in the DTO file</li>
<li>Customize the React component with your UI</li>
</ol>
</div>
<h2 id="end-to-end-type-safety">End-to-End Type Safety</h2>
<p>
One of Ruskit's core features is complete end-to-end type safety between your Rust backend and TypeScript frontend.
This means that any changes to your Rust DTOs are automatically reflected in your TypeScript types, preventing type mismatches.
</p>
<h3>How It Works</h3>
<p>
When you build your project, Ruskit automatically:
</p>
<ol>
<li>Scans your Rust codebase for DTOs marked with <code>#[derive(TS)]</code></li>
<li>Generates corresponding TypeScript interfaces in <code>resources/js/types/generated.ts</code></li>
<li>Updates the TypeScript types whenever your Rust DTOs change</li>
</ol>
<h3>Benefits</h3>
<ul>
<li>Catch type errors at compile time, not runtime</li>
<li>Automatic TypeScript type generation - no manual type maintenance</li>
<li>Full IDE support with autocompletion and type hints</li>
<li>Refactoring safety - renaming props in Rust automatically flags TypeScript errors</li>
</ul>
<div class="code-block">
<h4>Example: Type-Safe Data Flow</h4>
<pre class="line-numbers"><code class="language-rust">// 1. Define your DTO in Rust
#[derive(Serialize, TS)]
#[ts(export)]
pub struct UserData {
pub id: i32,
pub username: String,
pub email: String,
pub role: UserRole,
}
#[derive(Serialize, TS)]
#[ts(export)]
pub enum UserRole {
Admin,
User,
Guest,
}</code></pre>
</div>
<div class="code-block">
<h4>Generated TypeScript Types:</h4>
<pre class="line-numbers"><code class="language-typescript">// Automatically generated in resources/js/types/generated.ts
export interface UserData {
id: number;
username: string;
email: string;
role: UserRole;
}
export enum UserRole {
Admin = "Admin",
User = "User",
Guest = "Guest",
}</code></pre>
</div>
<div class="code-block">
<h4>Type-Safe Usage in React:</h4>
<pre class="line-numbers"><code class="language-typescript">import { UserData } from '../types/generated'
interface Props {
user: UserData;
}
export function UserProfile({ user }: Props) {
return (
<div>
<h1>{user.username}</h1>
<p>{user.email}</p>
{user.role === 'Admin' && (
<AdminPanel />
)}
</div>
)
}</code></pre>
</div>
<div class="info-block">
<h4>Type Safety Guarantees</h4>
<ul>
<li>TypeScript will error if you try to access non-existent properties</li>
<li>Enum values are type-safe and exhaustively checked</li>
<li>Nested objects and arrays maintain their type information</li>
<li>IDE provides accurate autocompletion for all properties</li>
</ul>
</div>
<h2 id="workflow">Development Workflow</h2>
<div class="info-block">
<h4>Starting the Development Server</h4>
<pre class="line-numbers"><code class="language-shell-session">$ cargo make dev</code></pre>
<p>This single command will start both the Rust backend and the Vite development server with hot reloading enabled.</p>
</div>
<div class="info-block">
<h4>Building for Production</h4>
<pre class="line-numbers"><code class="language-shell-session">$ npm run build</code></pre>
<p>This will build your frontend assets for production.</p>
</div>
<h2 id="best-practices">Best Practices</h2>
<div class="grid">
<div class="card">
<h4>Component Organization</h4>
<ul class="list">
<li>Keep components in <code>resources/js/components</code></li>
<li>Use TypeScript interfaces for prop types</li>
<li>Implement proper error boundaries</li>
</ul>
</div>
<div class="card">
<h4>State Management</h4>
<ul class="list">
<li>Use Inertia.js for server-side state</li>
<li>React hooks for local state</li>
<li>Consider Zustand or Jotai for complex state</li>
</ul>
</div>
</div>
<h2 id="troubleshooting">Troubleshooting</h2>
<div class="warning-block">
<h4>Common Issues</h4>
<ul class="list">
<li>Run <code class="language-shell-session">tsc --noEmit</code> to check for type errors</li>
<li>Make sure all dependencies have type definitions</li>
<li>Check your <code>tsconfig.json</code> configuration</li>
</ul>
</div>
</div>
{% endblock %}
{% block sidebar %}
<div class="nav-group">
<div class="nav-group-title">Frontend Guide</div>
<div class="nav-items">
<a href="#react-with-inertia">React with Inertia.js</a>
<a href="#type-safe-props" class="sub-item">Type-Safe Props</a>
<a href="#creating-pages" class="sub-item">Creating Pages</a>
<a href="#type-safety-benefits" class="sub-item">Type Safety Benefits</a>
<a href="#tailwind">Tailwind CSS</a>
<a href="#workflow">Development Workflow</a>
<a href="#best-practices">Best Practices</a>
<a href="#troubleshooting">Troubleshooting</a>
</div>
</div>
{% endblock %}