react-app-building — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited react-app-building (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Patterns for building complete React applications: routing, layouts, forms, data fetching, mutations, and the server/client boundary. Aligned with the React Server Components (RSC) model and App Router conventions.
Routes map to folders with page.tsx files. Colocate UI components, tests, and utilities directly within route folders.
app/
├── layout.tsx # Root layout (required) — wraps <html> and <body>
├── page.tsx # Home route (/)
├── dashboard/
│ ├── layout.tsx # Dashboard layout — persists across child navigation
│ └── page.tsx # /dashboard
└── settings/
└── page.tsx # /settingsLayouts wrap nested pages via children prop. On navigation, only the page re-renders — the layout preserves client-side React state (partial rendering).
Use the <Link> component for client-side transitions without full page refresh. In production, routes are automatically prefetched when <Link> enters the viewport.
Declare server-side async functions with 'use server' directive. Pass directly to <form action={...}> — receives native FormData automatically.
Progressive enhancement: Forms work even before JavaScript loads on the client. Security: Server Actions are public POST endpoints. Always verify authentication AND authorization inside every action. Never trust client-side validation alone.
Validate FormData with Zod before database operations:
revalidatePath(path) — purge client cache, fetch fresh server dataredirect(path) — navigate to updated pagebind to attach IDs to actions (not hidden inputs)React 19 introduces first-class form state management via three companion hooks. These replace the manual useState + useEffect fetch pattern for mutations.
useActionState — Action State ManagerWraps a Server Action and manages its pending state, result, and error automatically:
'use client';
import { useActionState } from 'react';
import { createPost } from './actions';
const initialState = { errors: {}, message: null };
export function CreatePostForm() {
const [state, formAction, isPending] = useActionState(createPost, initialState);
return (
<form action={formAction}>
<input name="title" aria-describedby="title-error" />
{state.errors?.title && (
<span id="title-error" aria-live="polite">{state.errors.title}</span>
)}
<button type="submit" disabled={isPending}>
{isPending ? 'Saving…' : 'Save'}
</button>
</form>
);
}useFormStatus — Parent Form Pending StateReads the pending state of the nearest parent `<form>` — no prop drilling required:
'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving…' : 'Save'}
</button>
);
}
// Must be rendered inside a <form> — works with Server or Client ActionsuseOptimistic — Instant UI UpdatesUpdates UI immediately on user interaction and rolls back if the server action fails:
'use client';
import { useOptimistic } from 'react';
function TodoList({ todos, addTodoAction }) {
const [optimisticTodos, addOptimistic] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);
async function formAction(formData) {
const title = formData.get('title') as string;
addOptimistic({ id: crypto.randomUUID(), title }); // instant UI update
await addTodoAction(formData); // actual server call
}
return (
<>
{optimisticTodos.map(t => (
<li key={t.id} style={{ opacity: t.pending ? 0.5 : 1 }}>{t.title}</li>
))}
<form action={formAction}>
<input name="title" />
<SubmitButton />
</form>
</>
);
}See references/detailed-patterns.md for full form examples with Zod validation, error handling, and redirect patterns.
Use async/await directly — no useEffect or useState needed. Keeps secrets on the server, reduces client bundle.
Avoid unintentional request waterfalls where requests block each other. Use Promise.all() for independent requests:
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);Prevent slow queries from blocking the entire page:
loading.tsx — streams entire route segment with a fallback<Suspense fallback={<Skeleton />}> — granular streaming per componentWhen client-side fetching is necessary:
use API to resolve server-passed promises in <Suspense>"use client")onClick, onChange, event handlersuseState, useReduceruseEffectlocalStorage, window, IntersectionObserver"use client" makes the file AND all its imports part of the client bundle"use client" as deep in the tree as possible to minimize bundle"use client" need a wrapper Client Componentserver-only package to prevent server code from leaking to client~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.