Get the FREE Ultimate OpenClaw Setup Guide →

frontend

npx machina-cli add skill mrsknetwork/supernova/frontend --openclaw
Files (1)
SKILL.md
7.2 KB

Frontend Engineering

Default Stack (Ask First)

Before applying anything, ask:

"Can I use the Supernova frontend stack? Next.js 14 (App Router) + TypeScript strict + Tailwind CSS v3 + Shadcn/ui + Radix UI. Or do you have an existing frontend stack I should match?"

If a package.json exists with next, react, or a different framework already installed, detect and match it.

Progressive Disclosure

  • Load references/nextjs-app-router.md for advanced App Router patterns (parallel routes, intercepting routes, streaming).
  • Load references/shadcn-components.md for Shadcn/ui component list and usage patterns.

SOP: Next.js 14 App Router Implementation

Step 1 - Project Setup (New Projects Only)

npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
npx shadcn@latest init

When shadcn init prompts:

  • Style: Default
  • Base color: Slate
  • CSS variables: Yes

This creates the components.json config. Components are added per-use with npx shadcn@latest add <component>.

Step 2 - App Router File Conventions

Understand and use these special files. Do not create arbitrary layouts:

FilePurpose
app/layout.tsxRoot layout - HTML shell, global fonts, providers
app/page.tsxRoute page component (Server Component by default)
app/loading.tsxStreaming skeleton shown during data fetch
app/error.tsxError boundary for the route segment (must be "use client")
app/not-found.tsxCustom 404 for the route segment
app/(group)/Route group - organizes routes without affecting URL
app/[param]/Dynamic route segment

Step 3 - Server Component vs Client Component Decision

This is the most important architectural decision in Next.js 14. Default to Server Components:

Use Server Component when (no "use client" directive needed):

  • Fetching data directly (database, ORM, external API)
  • Rendering static or session-based content
  • Accessing server-only resources (env vars, file system)

Use Client Component ("use client" at top of file) when:

  • Using React hooks (useState, useEffect, useRef, useContext)
  • Adding event listeners (onClick, onChange)
  • Using browser APIs (window, localStorage)
  • Using Shadcn/ui interactive components (they are all Client Components)

The boundary rule: Push "use client" as deep into the tree as possible. A parent Server Component can import and render a Client Component, but cannot import a Server Component into a Client Component.

// app/dashboard/page.tsx - Server Component (default)
import { DashboardStats } from "@/components/DashboardStats"; // Server Component - data fetch
import { InteractiveChart } from "@/components/InteractiveChart"; // Client Component

export default async function DashboardPage() {
  const stats = await getStats(); // direct DB call - works in Server Component
  return (
    <main>
      <DashboardStats stats={stats} />
      <InteractiveChart /> {/* boundary: "use client" is inside this component */}
    </main>
  );
}

Step 4 - TypeScript Strict Mode Patterns

Enable in tsconfig.json:

{ "compilerOptions": { "strict": true } }

Component with typed props:

// components/UserCard.tsx
interface UserCardProps {
  userId: string;
  displayName: string;
  avatarUrl: string | null;
  onRemove?: (userId: string) => void;
}

export function UserCard({ userId, displayName, avatarUrl, onRemove }: UserCardProps) {
  return (
    <div className="flex items-center gap-3 p-4 rounded-lg border border-border">
      <img src={avatarUrl ?? "/default-avatar.png"} alt={`${displayName}'s avatar`} className="h-10 w-10 rounded-full" />
      <span className="text-sm font-medium">{displayName}</span>
      {onRemove && (
        <button onClick={() => onRemove(userId)} aria-label={`Remove ${displayName}`} className="ml-auto">
          Remove
        </button>
      )}
    </div>
  );
}

Never use any. Use unknown + type guard for dynamic shapes. Use interface for props, type for unions.

Step 5 - Shadcn/ui Component Usage

Install components as needed, not all at once:

npx shadcn@latest add button dialog form input table toast

Use Shadcn/ui for: forms, modals, dropdowns, tables, toasts, navigation menus. Build custom components on top of Radix UI primitives for headless needs.

// Correct Shadcn/ui form pattern with react-hook-form + zod
"use client";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";

const schema = z.object({ email: z.string().email(), password: z.string().min(8) });
type FormData = z.infer<typeof schema>;

export function LoginForm() {
  const form = useForm<FormData>({ resolver: zodResolver(schema) });
  async function onSubmit(data: FormData) { /* call API */ }
  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField control={form.control} name="email" render={({ field }) => (
          <FormItem>
            <FormLabel>Email</FormLabel>
            <FormControl><Input type="email" {...field} /></FormControl>
            <FormMessage />
          </FormItem>
        )} />
        <Button type="submit" disabled={form.formState.isSubmitting}>Sign In</Button>
      </form>
    </Form>
  );
}

Step 6 - Data Fetching Patterns

In Server Components (preferred):

async function ProductsPage() {
  const products = await fetch("https://api.example.com/products", { next: { revalidate: 60 } }).then(r => r.json());
  return <ProductList products={products} />;
}

In Client Components (for user-triggered or dynamic data):

"use client";
import { useQuery } from "@tanstack/react-query";

function ProductList() {
  const { data, isLoading } = useQuery({ queryKey: ["products"], queryFn: () => fetch("/api/products").then(r => r.json()) });
  if (isLoading) return <Skeleton />;
  return data.map(p => <ProductCard key={p.id} {...p} />);
}

Step 7 - Tailwind CSS Conventions

// tailwind.config.ts - always define semantic colors
import type { Config } from "tailwindcss";
export default {
  content: ["./src/**/*.{ts,tsx}"],
  theme: {
    extend: {
      colors: {
        brand: { DEFAULT: "hsl(var(--brand))", foreground: "hsl(var(--brand-foreground))" },
      },
    },
  },
} satisfies Config;

Use Tailwind utility classes in JSX directly. Do not create custom CSS classes unless animating or working with pseudo-elements that Tailwind cannot express. Avoid style={{}} inline styles except for truly dynamic values (e.g., progress bar width).

Source

git clone https://github.com/mrsknetwork/supernova/blob/main/skills/frontend/SKILL.mdView on GitHub

Overview

Provides a practical blueprint for building React/Next.js UIs with Next.js 14 App Router, TypeScript strict mode, Tailwind CSS v3, Shadcn UI, and Radix UI. It emphasizes server versus client component boundaries, typed props, accessible markup, and integrated form handling. It also advises verifying the existing stack before applying defaults to align with current projects.

How This Skill Works

The skill outlines a pattern where server components handle data fetching and static rendering while client components handle interactivity. It enforces strict TypeScript props, uses Tailwind v3 with Shadcn UI and Radix UI for cohesive styling, and requires accessible markup. The approach guides developers to default to server components, then add a deeply nested client boundary where hooks or browser APIs are needed, ensuring proper App Router conventions and type safety.

When to Use It

  • Starting a new Next.js 14 project with App Router, TS strict mode, Tailwind v3, Shadcn UI, and Radix UI.
  • Building data-driven pages where server components fetch data and client components render interactive UI.
  • Creating reusable UI components with typed props and accessible markup across the app.
  • Implementing forms and form handling with integrated validation and accessible controls.
  • Styling and composing components with a consistent design system using Tailwind, Shadcn UI, and Radix UI.

Quick Start

  1. Step 1: Initialize a new Next.js app with TypeScript, Tailwind, App Router, and an alias like @/* using the provided commands.
  2. Step 2: Adhere to App Router file conventions such as app/layout.tsx and app/page.tsx, avoiding arbitrary layouts and using server components by default.
  3. Step 3: Decide on server vs client components: use server components for data fetching and rendering; introduce use client only where hooks or browser APIs are required, placing the directive deep in the tree.

Best Practices

  • Default to server components to maximize performance; only add a client component when interactivity requires hooks or browser APIs.
  • Keep use client directives as deep in the tree as possible; a server component can render a client component, but not vice versa.
  • Define explicit prop interfaces for all UI components and enable TypeScript strict mode to catch type mismatches early.
  • Use accessible markup for all interactive elements; provide labels, proper ARIA roles, keyboard navigation, and semantic HTML.
  • Leverage App Router conventions for layout and routing (app/layout.tsx, app/page.tsx) and prefer data fetching at the server boundary when possible.

Example Use Cases

  • Dashboard page where server components fetch stats and a client component renders an InteractiveChart with client-side interactivity.
  • User profile page with server-fetched data and a client-side editable form that updates state without a full reload.
  • Product listing with server-rendered data augmented by client-side filters and sort controls.
  • Multi-step signup or settings form using accessible form controls and integrated validation via Radix UI components.
  • Consistent UI library usage across pages, combining Tailwind v3 utility classes with Shadcn UI and Radix UI components.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers