← Back to list

agentic-jumpstart-performance
by webdevcody
⭐ 15🍴 10📅 Jan 22, 2026
SKILL.md
name: agentic-jumpstart-performance description: Performance optimization patterns for TanStack Start with React 19, Vite, Drizzle ORM, and TanStack Query. Use when optimizing page load times, database queries, bundle size, caching, memoization, lazy loading, or when the user mentions performance, speed, optimization, or slow.
Performance Optimization
React 19 Performance Patterns
Code Splitting with Lazy Loading
import { lazy, Suspense } from "react";
// Lazy load heavy components
const HeavyEditor = lazy(() => import("./HeavyEditor"));
const DataVisualization = lazy(() => import("./DataVisualization"));
function Dashboard() {
return (
<Suspense fallback={<LoadingSkeleton />}>
<HeavyEditor />
</Suspense>
);
}
Strategic Memoization
import { memo, useMemo, useCallback, useDeferredValue } from "react";
// Memoize expensive computations
const expensiveResult = useMemo(() => {
return processLargeDataset(data);
}, [data]);
// Memoize callbacks passed to children
const handleClick = useCallback((id: number) => {
updateItem(id);
}, [updateItem]);
// Defer non-critical updates
const deferredSearchTerm = useDeferredValue(searchTerm);
// Memoize components that receive stable props
const MemoizedList = memo(function List({ items }: { items: Item[] }) {
return items.map(item => <ListItem key={item.id} item={item} />);
});
Avoid Re-renders
// BAD: Creates new object every render
<Component style={{ color: "red" }} />
// GOOD: Define outside or memoize
const style = { color: "red" };
<Component style={style} />
// BAD: Creates new function every render
<Button onClick={() => handleClick(id)} />
// GOOD: Use useCallback or define handler
const handleButtonClick = useCallback(() => handleClick(id), [id]);
<Button onClick={handleButtonClick} />
TanStack Query Optimization
Caching Configuration
import { queryOptions } from "@tanstack/react-query";
// Configure stale time based on data freshness needs
export const segmentsQueryOptions = () =>
queryOptions({
queryKey: ["segments"],
queryFn: () => getSegmentsFn(),
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 30 * 60 * 1000, // 30 minutes cache
});
// For frequently changing data
export const statsQueryOptions = () =>
queryOptions({
queryKey: ["stats"],
queryFn: () => getStatsFn(),
staleTime: 30 * 1000, // 30 seconds
refetchInterval: 60 * 1000, // Auto-refetch every minute
});
Prefetching
// Prefetch on route load
export const Route = createFileRoute("/courses/$courseId")({
loader: async ({ params, context }) => {
await context.queryClient.ensureQueryData(
courseQueryOptions(params.courseId)
);
},
component: CoursePage,
});
// Prefetch on hover
function CourseCard({ course }) {
const queryClient = useQueryClient();
const handleMouseEnter = () => {
queryClient.prefetchQuery(courseQueryOptions(course.id));
};
return (
<Link to={`/courses/${course.id}`} onMouseEnter={handleMouseEnter}>
{course.title}
</Link>
);
}
Optimistic Updates
const mutation = useMutation({
mutationFn: updateSegmentFn,
onMutate: async (newData) => {
await queryClient.cancelQueries({ queryKey: ["segments"] });
const previous = queryClient.getQueryData(["segments"]);
queryClient.setQueryData(["segments"], (old) =>
old.map((s) => (s.id === newData.id ? { ...s, ...newData } : s))
);
return { previous };
},
onError: (err, newData, context) => {
queryClient.setQueryData(["segments"], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["segments"] });
},
});
Database Query Optimization
Select Only Required Columns
// BAD: Fetches all columns
const users = await database.select().from(users);
// GOOD: Select only what you need
const users = await database
.select({ id: users.id, name: users.name, email: users.email })
.from(users);
Use Indexes Effectively
// Ensure indexes exist for frequently queried columns
// In schema definition:
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
createdAt: timestamp("created_at").defaultNow(),
}, (table) => ({
emailIdx: index("email_idx").on(table.email),
createdAtIdx: index("created_at_idx").on(table.createdAt),
}));
Avoid N+1 Queries
// BAD: N+1 query pattern
const segments = await getSegments();
for (const segment of segments) {
segment.attachments = await getAttachments(segment.id);
}
// GOOD: Single query with join
const segmentsWithAttachments = await database
.select()
.from(segments)
.leftJoin(attachments, eq(segments.id, attachments.segmentId));
// Or use Drizzle's with clause
const result = await database.query.segments.findMany({
with: {
attachments: true,
module: true,
},
});
Pagination
export async function getPaginatedUsers(page: number, limit: number) {
const offset = (page - 1) * limit;
const [data, [{ count }]] = await Promise.all([
database
.select()
.from(users)
.orderBy(desc(users.createdAt))
.limit(limit)
.offset(offset),
database.select({ count: sql`count(*)` }).from(users),
]);
return {
data,
pagination: {
page,
limit,
total: Number(count),
totalPages: Math.ceil(Number(count) / limit),
},
};
}
Bundle Optimization
Vite Configuration
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
// Split vendor chunks
"react-vendor": ["react", "react-dom"],
"query-vendor": ["@tanstack/react-query"],
"ui-vendor": ["@radix-ui/react-dialog", "@radix-ui/react-dropdown-menu"],
},
},
},
// Enable minification
minify: "terser",
terserOptions: {
compress: {
drop_console: true, // Remove console.logs in production
},
},
},
});
Dynamic Imports for Large Libraries
// Dynamically import heavy libraries
async function processMarkdown(content: string) {
const { marked } = await import("marked");
return marked(content);
}
// For React components
const RichTextEditor = lazy(() => import("./RichTextEditor"));
const ChartComponent = lazy(() => import("recharts").then(mod => ({
default: mod.ResponsiveContainer
})));
Image & Media Optimization
Lazy Loading Images
function OptimizedImage({ src, alt }: { src: string; alt: string }) {
return (
<img
src={src}
alt={alt}
loading="lazy"
decoding="async"
/>
);
}
Video Loading Strategy
function VideoPlayer({ videoKey }: { videoKey: string }) {
const [videoUrl, setVideoUrl] = useState<string | null>(null);
// Load video URL only when component is visible
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadVideoUrl(videoKey).then(setVideoUrl);
observer.disconnect();
}
});
observer.observe(containerRef.current);
return () => observer.disconnect();
}, [videoKey]);
return videoUrl ? <video src={videoUrl} controls /> : <VideoSkeleton />;
}
Tailwind CSS Optimization
Purge Unused CSS
Tailwind v4 automatically purges unused styles in production. Ensure your content paths are correct:
// tailwind.config.ts
export default {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./src/components/**/*.{js,ts,jsx,tsx}",
],
};
Use CSS Variables for Theming
/* Avoid redundant utility classes */
:root {
--primary: theme(colors.blue.600);
--primary-hover: theme(colors.blue.700);
}
Memory Leak Prevention
Cleanup Effects
useEffect(() => {
const controller = new AbortController();
fetchData({ signal: controller.signal })
.then(setData)
.catch((err) => {
if (err.name !== "AbortError") {
setError(err);
}
});
return () => controller.abort();
}, []);
Event Listener Cleanup
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
Performance Checklist
- Heavy components are lazy loaded with Suspense
- TanStack Query has appropriate staleTime/gcTime
- Database queries select only needed columns
- Joins are used instead of N+1 queries
- Large lists are paginated
- Bundle chunks are optimized for caching
- Images use loading="lazy"
- Effects clean up subscriptions and abort controllers
- Memoization is used strategically (not everywhere)
- Prefetching is used for predictable navigation
Score
Total Score
65/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
✓フォーク
10回以上フォークされている
+5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon