React Best Practices
ryokun6/ryos
React performance optimization guidelines from Vercel Engineering (vercel-labs/agent-skills).
Vite + React for fast modern web development - build config, HMR, hooks, state management, and performance patterns When user works with Vite, React, creates components, manages state, uses hooks…
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install shepherdjerred/monorepo vite-react-helper --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .claude/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .claude/skills/vite-react-helper && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .claude/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helperType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install shepherdjerred/monorepo vite-react-helper --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .agents/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .agents/skills/vite-react-helper && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .agents/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install shepherdjerred/monorepo vite-react-helper --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .cursor/skills/vite-react-helper && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .cursor/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/shepherdjerred/monorepo.git --path packages/dotfiles/dot_agents/skills/vite-react-helper--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install shepherdjerred/monorepo vite-react-helper --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .gemini/skills/vite-react-helper && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .gemini/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install shepherdjerred/monorepo vite-react-helperInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .github/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .github/skills/vite-react-helper && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .github/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add shepherdjerred/monorepo --skill vite-react-helper -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install shepherdjerred/monorepo vite-react-helper --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/shepherdjerred/monorepo.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/packages/dotfiles/dot_agents/skills/vite-react-helper .opencode/skills/vite-react-helper && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "vite-react-helper" agent skill from https://github.com/shepherdjerred/monorepo/tree/main/packages/dotfiles/dot_agents/skills/vite-react-helper into .opencode/skills/vite-react-helper/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vite-react-helper", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
vite-react-helperVite + React for fast modern web development - build config, HMR, hooks, state management, and performance patterns When user works with Vite, React, creates components, manages state, uses hooks…
Vite React Helper is an agent skill from shepherdjerred/monorepo. Vite + React for fast modern web development - build config, HMR, hooks, state management, and performance patterns When user works with Vite, React, creates components, manages state, uses hooks, or configures Vite builds
Its SKILL.md is about 4.8k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files, including reference files (for example `references/build-config.md`, `references/react-patterns.md` and `references/vercel-react-best-practices.md`).
It sits in Frontend & Design, covering State management. It works with React, Vite and Vercel. The repository describes itself as: Monorepo for all of my projects. The licence is GPL-3.0.
10 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 46ddf2f. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
bunnpmFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use npm, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Vite React Helper loads about 4.8k tokens when it runs, and up to ~9.3k if it reads all its reference files. Until then it costs about 60 tokens; SKILL.md has 351 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
# .env# .env.development# .env.productionAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from shepherdjerred/monorepo at commit 46ddf2f, republished under its GPL-3.0 licence (© shepherdjerred). 351 words, ~4,831 tokens.
.claude/skills/vite-react-helper/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.use() hook: Read promises and context in render<title>, <meta> in components# Bun
bun create vite my-app --template react-ts
cd my-app
bun install
bun run dev| Template | Description |
|---|---|
react | React with JavaScript |
react-ts | React with TypeScript |
react-swc | React + SWC (faster builds) |
react-swc-ts | React + SWC + TypeScript |
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
open: true,
},
build: {
outDir: "dist",
sourcemap: true,
},
});import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
react({
// JSX runtime (automatic or classic)
jsxRuntime: "automatic",
// Custom JSX import source (e.g., for Emotion)
jsxImportSource: "@emotion/react",
// Babel plugins for features like decorators
babel: {
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
},
// File patterns to include
include: /\.(mdx|js|jsx|ts|tsx)$/,
}),
],
});import react from "@vitejs/plugin-react-swc";
export default defineConfig({
plugins: [
react({
// Enable React Refresh (default: true)
devTarget: "es2022",
// SWC plugins
plugins: [["@swc/plugin-emotion", {}]],
}),
],
});# .env
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
# .env.development
VITE_API_URL=http://localhost:8080
# .env.production
VITE_API_URL=https://api.prod.example.com// Access in code (only VITE_ prefixed vars exposed)
const apiUrl = import.meta.env.VITE_API_URL;
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;
const mode = import.meta.env.MODE;// TypeScript definitions (vite-env.d.ts)
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}// vite.config.ts
import path from "path";
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@components": path.resolve(__dirname, "./src/components"),
"@hooks": path.resolve(__dirname, "./src/hooks"),
},
},
});// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@hooks/*": ["src/hooks/*"]
}
}
}// Props with TypeScript
interface ButtonProps {
label: string;
variant?: "primary" | "secondary";
disabled?: boolean;
onClick?: () => void;
children?: React.ReactNode;
}
function Button({
label,
variant = "primary",
disabled = false,
onClick,
children,
}: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
disabled={disabled}
onClick={onClick}
>
{children ?? label}
</button>
);
}import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
// Functional updates for derived state
const increment = () => setCount((prev) => prev + 1);
const decrement = () => setCount((prev) => prev - 1);
return (
<div>
<p>Count: {count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}import { useReducer } from "react";
interface State {
count: number;
step: number;
}
type Action =
| { type: "increment" }
| { type: "decrement" }
| { type: "setStep"; step: number }
| { type: "reset" };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "increment":
return { ...state, count: state.count + state.step };
case "decrement":
return { ...state, count: state.count - state.step };
case "setStep":
return { ...state, step: action.step };
case "reset":
return { count: 0, step: 1 };
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}import { useEffect, useState } from "react";
function ChatRoom({ roomId }: { roomId: string }) {
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
// Setup
const connection = createConnection(roomId);
connection.connect();
connection.on("message", (msg) => {
setMessages((prev) => [...prev, msg]);
});
// Cleanup (runs on unmount or before next effect)
return () => {
connection.disconnect();
};
}, [roomId]); // Re-run when roomId changes
return <MessageList messages={messages} />;
}import { useRef, useEffect } from "react";
function VideoPlayer({ src }: { src: string }) {
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
// Direct DOM manipulation
if (videoRef.current) {
videoRef.current.play();
}
}, [src]);
return <video ref={videoRef} src={src} />;
}import { createContext, useContext, useState, ReactNode } from "react";
interface ThemeContextType {
theme: "light" | "dark";
toggle: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<"light" | "dark">("light");
const toggle = () => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within ThemeProvider");
}
return context;
}
// Usage
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
function ThemedButton() {
const { theme, toggle } = useTheme();
return <button onClick={toggle}>Current: {theme}</button>;
}When reviewing React code, selectively apply the repo-owned adaptation in
references/vercel-react-best-practices.md: remove request waterfalls, defer
non-critical work, keep initial bundles small, avoid unnecessary re-renders,
and measure before optimizing. Keep this guidance compatible with the repo's
Bun/Vite toolchain and local tests; do not introduce Next.js, Vercel deployment,
or Vercel-specific optimization workflows unless the project already uses them.
import { useMemo } from "react";
function FilteredList({ items, filter }: Props) {
// Only recalculates when items or filter changes
const filteredItems = useMemo(() => {
return items.filter((item) => item.name.includes(filter));
}, [items, filter]);
return (
<ul>
{filteredItems.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}import { useCallback, memo } from "react";
// Memoized child component
const ExpensiveChild = memo(function ExpensiveChild({
onClick,
}: {
onClick: () => void;
}) {
console.log("Child rendered");
return <button onClick={onClick}>Click</button>;
});
function Parent() {
const [count, setCount] = useState(0);
// Stable function reference
const handleClick = useCallback(() => {
console.log("Clicked");
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>Increment</button>
<ExpensiveChild onClick={handleClick} />
</div>
);
}import { lazy, Suspense } from "react";
// Dynamic import - only loads when needed
const HeavyChart = lazy(() => import("./HeavyChart"));
const AdminPanel = lazy(() => import("./AdminPanel"));
function App() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
<Suspense fallback={<div>Loading chart...</div>}>
{showChart && <HeavyChart />}
</Suspense>
<Suspense fallback={<div>Loading admin...</div>}>
<AdminPanel />
</Suspense>
</div>
);
}import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}import { useState, useEffect } from "react";
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: Error | null;
refetch: () => void;
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url);
if (!response.ok) throw new Error("Failed to fetch");
const json = await response.json();
setData(json);
} catch (err) {
setError(err instanceof Error ? err : new Error("Unknown error"));
} finally {
setLoading(false);
}
};
useEffect(() => {
let ignore = false;
(async () => {
try {
const response = await fetch(url);
const json = await response.json();
if (!ignore) {
setData(json);
setLoading(false);
}
} catch (err) {
if (!ignore) {
setError(err instanceof Error ? err : new Error("Unknown error"));
setLoading(false);
}
}
})();
return () => {
ignore = true;
};
}, [url]);
return { data, loading, error, refetch: fetchData };
}
// Usage
function UserProfile({ userId }: { userId: string }) {
const { data: user, loading, error } = useFetch<User>(`/api/users/${userId}`);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
if (!user) return null;
return <div>{user.name}</div>;
}import { useState, useEffect } from "react";
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
try {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
} catch {
return initialValue;
}
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const;
}
// Usage
function Settings() {
const [theme, setTheme] = useLocalStorage("theme", "light");
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle
</button>
);
}import { useState, useEffect } from "react";
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchInput() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
if (debouncedQuery) {
searchAPI(debouncedQuery);
}
}, [debouncedQuery]);
return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
}// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Vendor chunks
"react-vendor": ["react", "react-dom"],
router: ["react-router-dom"],
ui: ["@radix-ui/react-dialog", "@radix-ui/react-dropdown-menu"],
},
},
},
},
});# Install analyzer
npm install -D rollup-plugin-visualizer
# vite.config.ts
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
plugins: [
react(),
visualizer({ open: true }),
],
});export default defineConfig({
build: {
// Target modern browsers
target: "esnext",
// Minification
minify: "esbuild", // or "terser" for smaller bundles
// CSS code splitting
cssCodeSplit: true,
// Source maps for debugging
sourcemap: true,
// Asset inlining threshold (bytes)
assetsInlineLimit: 4096,
// Chunk size warnings
chunkSizeWarningLimit: 500,
},
});// vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/test/setup.ts",
css: true,
},
});// src/test/setup.ts
import "@testing-library/jest-dom";import { render, screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import Counter from "./Counter";
describe("Counter", () => {
it("renders initial count", () => {
render(<Counter initialCount={5} />);
expect(screen.getByText("Count: 5")).toBeInTheDocument();
});
it("increments on click", () => {
render(<Counter initialCount={0} />);
fireEvent.click(screen.getByRole("button", { name: "+" }));
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
it("calls onChange when count changes", () => {
const handleChange = vi.fn();
render(<Counter initialCount={0} onChange={handleChange} />);
fireEvent.click(screen.getByRole("button", { name: "+" }));
expect(handleChange).toHaveBeenCalledWith(1);
});
});import { useState, FormEvent } from "react";
interface FormData {
email: string;
password: string;
}
function LoginForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
const [formData, setFormData] = useState<FormData>({
email: "",
password: "",
});
const [errors, setErrors] = useState<Partial<FormData>>({});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
setErrors((prev) => ({ ...prev, [name]: undefined }));
};
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
// Validation
const newErrors: Partial<FormData> = {};
if (!formData.email) newErrors.email = "Email required";
if (!formData.password) newErrors.password = "Password required";
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
onSubmit(formData);
};
return (
<form onSubmit={handleSubmit}>
<input
name="email"
type="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <span>{errors.email}</span>}
<input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && <span>{errors.password}</span>}
<button type="submit">Login</button>
</form>
);
}import { Component, ReactNode } from "react";
interface Props {
children: ReactNode;
fallback: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error("Error caught:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ErrorBoundary fallback={<h1>Something went wrong</h1>}>
<MyComponent />
</ErrorBoundary>
);
}useMemo and useCallbacklazy() and Suspense© shepherdjerred, GPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 3 other files (references) in packages/dotfiles/dot_agents/skills/vite-react-helper of shepherdjerred/monorepo.
Open the folder on GitHubat commit 46ddf2f
Vite React Helper next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Vite React Helper this skillshepherdjerred/monorepo | 112 | — | ~4.8k | Automated safety check: Notes | GPL-3.0 | |
| React Best Practicesryokun6/ryos | 1.3k | — | ~2k | Automated safety check: Pass | MIT | |
| Vercel Geist Design Systemsyhily/kobato | 114 | — | ~3.3k | Automated safety check: Pass | MIT | |
| React Generate Skilljiushiwon/wg-skills | 110 | — | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| Cursor BYOK Frontend Guideleookun/cursor-byok | 3.2k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Frontend Design UltimateaAAaqwq/AGI-Super-Team | 105 | 2 repos | ~2.7k | Automated safety check: Pass | MIT |
ryokun6/ryos
React performance optimization guidelines from Vercel Engineering (vercel-labs/agent-skills).
syhily/kobato
Community skill for applying the Geist design system to Vercel-inspired rendered frontend UI across React/Next.js/Vite/Astro/Svelte/Vue/HTML/CSS, Tailwind, shadcn, and Radix.
jiushiwon/wg-skills
This skill should be used when the user wants to create a standardized React 18 + TypeScript + Vite + Zustand project from scratch.
leookun/cursor-byok
Sets the rules for building the Cursor BYOK desktop app's React and Tauri frontend, especially its HTTP boundary and component state architecture.
aAAaqwq/AGI-Super-Team
Create distinctive, production-grade static sites with React, Tailwind CSS, and shadcn/ui — no mockups needed.
getsentry/sentry-for-ai
Full Sentry SDK setup for React. An agent skill from getsentry/sentry-for-ai.
shepherdjerred/monorepo
Bun runtime APIs and current operational patterns for files, processes, modules, networking, databases, tests, and deployment.
shepherdjerred/monorepo
Current Bun test runner guidance for discovery, isolation, parallelism, sharding, changed tests, mocks, timers, snapshots, coverage, DOM Testing Library, and integration teardown.
shepherdjerred/monorepo
Current Bun workspace guidance for isolated and hoisted linkers, catalogs, filters, scripts, dependency classes, lockfiles, lifecycle trust, caches, publishing, TypeScript package exports, and…
shepherdjerred/monorepo
This skill should be used when the user asks to "deep research", "research this topic", "investigate thoroughly", "do a deep dive on", "comprehensive research on", "find everything about", "survey…
shepherdjerred/monorepo
This skill should be used when the user asks to "create a Figma design", "design in Figma", "make a Figma mockup", "create an app icon", "design UI", "render JSX to Figma", "export from Figma"…
shepherdjerred/monorepo
Current Fish shell scripting, functions, abbreviations, completions, variables, events, configuration, plugins, testing, and safety guidance.
Categories
Vite + React for fast modern web development - build config, HMR, hooks, state management, and performance patterns When user works with Vite, React, creates components, manages state, uses hooks…. Vite React Helper is an agent skill from shepherdjerred/monorepo.
Vite React Helper fits situations like: works with Vite; creates components; configures Vite builds.
Run `npx skills add shepherdjerred/monorepo --skill vite-react-helper -a claude-code`. Or copy the skill folder (packages/dotfiles/dot_agents/skills/vite-react-helper in shepherdjerred/monorepo) into .claude/skills/vite-react-helper in your project. Claude Code loads it when a task matches its description.
Run `npx skills add shepherdjerred/monorepo --skill vite-react-helper -a codex`. Or copy the skill folder (packages/dotfiles/dot_agents/skills/vite-react-helper in shepherdjerred/monorepo) into .agents/skills/vite-react-helper in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add shepherdjerred/monorepo --skill vite-react-helper -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/vite-react-helper, .gemini/skills/vite-react-helper, .github/skills/vite-react-helper and .opencode/skills/vite-react-helper in your project.
Going by SKILL.md and its folder, Vite React Helper needs the command-line tools its instructions call (bun and npm). Our summary lists: Node.js.
SKILL.md contains no URLs. Its commands use npm, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Vite React Helper is published under the GPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.8k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 4.5k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Vite React Helper: React Best Practices (ryokun6/ryos, 1.3k stars), Vercel Geist Design System (syhily/kobato, 114 stars), React Generate Skill (jiushiwon/wg-skills, 110 stars) and Cursor BYOK Frontend Guide (leookun/cursor-byok, 3.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
shepherdjerred (a GitHub user) maintains it in shepherdjerred/monorepo, which has 112 GitHub stars. The repository holds 63 skills in this directory. The repository was last updated on October 7, 2026.
Source: shepherdjerred/monorepo on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.