ruskit 0.1.5

A modern web framework for Rust inspired by Laravel
Documentation
{% 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 %}