react-19

prowler-cloud's avatarfrom prowler-cloud

React 19 patterns with React Compiler. Trigger: When writing React components - no useMemo/useCallback needed.

⭐12.5kstars🔀1.9kforks📁View on GitHub🕐Updated Jan 10, 2026

When & Why to Use This Skill

This Claude skill optimizes React 19 development by enforcing modern patterns compatible with the React Compiler. It streamlines the coding process by eliminating manual memoization (useMemo/useCallback), promoting Server Components by default, and implementing the latest React 19 features like the use() hook, Actions, and simplified ref handling. It ensures your codebase is performant, clean, and follows the latest architectural standards.

Use Cases

  • Refactoring existing React components to remove redundant useMemo and useCallback hooks, allowing the React Compiler to handle optimizations automatically.
  • Building modern web applications using Server Components and Server Actions to improve initial load times and simplify data mutations.
  • Implementing complex UI logic with the use() hook to handle asynchronous data fetching and conditional context access more elegantly.
  • Simplifying component libraries by passing refs as standard props, removing the boilerplate associated with forwardRef.
  • Ensuring consistent coding standards across a team by enforcing named imports and proper 'use client' directive placement.
namereact-19
description>
TriggerWhen writing React 19 components/hooks in .tsx (React Compiler rules, hook patterns, refs as props). If using Next.js App Router/Server Actions, also use nextjs-15.
licenseApache-2.0
authorprowler-cloud
version"1.0"
scope[root, ui]
auto_invoke"Writing React components"
allowed-toolsRead, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task

No Manual Memoization (REQUIRED)

// ✅ React Compiler handles optimization automatically
function Component({ items }) {
  const filtered = items.filter(x => x.active);
  const sorted = filtered.sort((a, b) => a.name.localeCompare(b.name));

  const handleClick = (id) => {
    console.log(id);
  };

  return <List items={sorted} onClick={handleClick} />;
}

// ❌ NEVER: Manual memoization
const filtered = useMemo(() => items.filter(x => x.active), [items]);
const handleClick = useCallback((id) => console.log(id), []);

Imports (REQUIRED)

// ✅ ALWAYS: Named imports
import { useState, useEffect, useRef } from "react";

// ❌ NEVER
import React from "react";
import * as React from "react";

Server Components First

// ✅ Server Component (default) - no directive
export default async function Page() {
  const data = await fetchData();
  return <ClientComponent data={data} />;
}

// ✅ Client Component - only when needed
"use client";
export function Interactive() {
  const [state, setState] = useState(false);
  return <button onClick={() => setState(!state)}>Toggle</button>;
}

When to use "use client"

  • useState, useEffect, useRef, useContext
  • Event handlers (onClick, onChange)
  • Browser APIs (window, localStorage)

use() Hook

import { use } from "react";

// Read promises (suspends until resolved)
function Comments({ promise }) {
  const comments = use(promise);
  return comments.map(c => <div key={c.id}>{c.text}</div>);
}

// Conditional context (not possible with useContext!)
function Theme({ showTheme }) {
  if (showTheme) {
    const theme = use(ThemeContext);
    return <div style={{ color: theme.primary }}>Themed</div>;
  }
  return <div>Plain</div>;
}

Actions & useActionState

"use server";
async function submitForm(formData: FormData) {
  await saveToDatabase(formData);
  revalidatePath("/");
}

// With pending state
import { useActionState } from "react";

function Form() {
  const [state, action, isPending] = useActionState(submitForm, null);
  return (
    <form action={action}>
      <button disabled={isPending}>
        {isPending ? "Saving..." : "Save"}
      </button>
    </form>
  );
}

ref as Prop (No forwardRef)

// ✅ React 19: ref is just a prop
function Input({ ref, ...props }) {
  return <input ref={ref} {...props} />;
}

// ❌ Old way (unnecessary now)
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />);