
Performance
Find and fix slow Flows/React UI performance—re-renders, heavy CDF queries, and bundle bloat—with measured before/after proof.
Install
npx skills add https://github.com/cognitedata/builder-skills --skill performanceWhat is this skill?
- Mandatory measure-first workflow: production build, preview, Lighthouse, and React Profiler baselines before edits
- Actively fixes re-renders by hunting inline object/function props and unstable dependencies in the TSX tree from App.tsx
- Targets CDF/Flows pain: inefficient queries, missing pagination, unbounded fetches, and large lists
- Covers bundle and runtime leaks: code splitting, lazy load, virtualization, debounce, and memory leak patterns
- Uses Read, Glob, Grep, Shell, and Write to change code—not audit-only reports
Adoption & trust: 1k installs on skills.sh; 4 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Journey fit
Performance hardening belongs on the Ship shelf because it is invoked when the app feels slow or launch-ready quality is at risk, even though fixes land in build-time code. Perf subphase is the canonical home for Lighthouse baselines, Profiler work, and load-time optimization—not passive linting.
Common Questions / FAQ
Is Performance safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Performance
# Performance Fix Systematically find and fix performance issues in **$ARGUMENTS** (or the whole app if no argument is given). Always measure first — never optimize blindly. --- ## Step 1 — Measure baseline before touching anything Run the production build and capture metrics before making any changes: ```bash pnpm run build pnpm run preview ``` Open the app in Chrome and capture: - **Lighthouse score** (Performance tab → Run audit) - **React Profiler** (React DevTools → Profiler → Record an interaction) - Note the components with the longest render times and highest render counts Record baseline numbers. Every fix must be measured against these. --- ## Step 2 — Find and fix unnecessary re-renders Read the component tree (start from `src/App.tsx`) and search for these patterns: ```bash grep -rn --include="*.tsx" \ -E "value=\{\{|onClick=\{\(\)" src/ ``` For each instance found, **apply the fix directly**: **Inline object/array creation in JSX → wrap with `useMemo`:** ```tsx // BAD — new object on every render causes children to re-render <Chart options={{ color: "red" }} /> // FIX — wrap with useMemo const chartOptions = useMemo(() => ({ color: "red" }), []); <Chart options={chartOptions} /> ``` **Event handlers recreated on every render → wrap with `useCallback`:** ```tsx // BAD <Button onClick={() => doSomething(id)} /> // FIX — wrap with useCallback const handleClick = useCallback(() => doSomething(id), [id]); <Button onClick={handleClick} /> ``` **Context that changes on every render → memoize the context value:** ```tsx // BAD — new object reference every render <MyContext.Provider value={{ user, sdk }}> // FIX — memoize the context value const ctxValue = useMemo(() => ({ user, sdk }), [user, sdk]); <MyContext.Provider value={ctxValue}> ``` Apply `React.memo` to pure presentational components that receive stable props. Do NOT wrap every component — only those confirmed to re-render unnecessarily via the Profiler. --- ## Step 3 — Find and fix DMS query patterns For **read-heavy** workloads, prefer APIs that hit the **search/Elasticsearch path** (`query` or `search` on instances) rather than `list` paths that stress **Postgres**. ```bash # Find all DMS instance API calls grep -rn --include="*.ts" --include="*.tsx" -E "instances\.(list|search|query|aggregate|retrieve)" src/ # Find direct SDK calls to other CDF resources grep -rn --include="*.ts" --include="*.tsx" -E "\.(assets|timeseries|events|files|sequences|relationships)\.(list|search|retrieve)" src/ ``` For each `instances.list` call in a read-heavy path (e.g. populating a table, dropdown, or search results), **rewrite it to use `instances.query`** with the equivalent filter. Preserve the existing filter logic but express it in the query API format: ```ts // BAD — instances.list hits Postgres, expensive for read-heavy UI const result = await client.instances.list({ instanceType: "node", filter: { equals: { property: ["node", "space"], value: "my-space" } }, limit: 100, }); // FIX — rewrite to instances.query which hits Elasticsearch const result = await client.instances.query({ with: { nodes: { nodes: { filter: { equals: { property: ["node", "space"], value: "my-space" } }, }, limit: 100, }, }, select: { nodes: {}, }, }); ``` | API used | When it's correct | Wh