Skip to main content

opik-frontend

React frontend patterns for Opik. Use when working in apps/opik-frontend, on components, state, or data fetching.

Quellinformationen

Repository
comet-ml/opik
Letzte Quellaktivität
22. Juli 2026 um 10:02
Erkannte Sprache von SKILL.md
Englisch
Sterne
22.634
Forks
1.864

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
8 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
opik-frontend
description
React frontend patterns for Opik. Use when working in apps/opik-frontend, on components, state, or data fetching.
# Opik Frontend ## Architecture Decisions - **Routing**: TanStack Router (file-based) - **Data fetching**: TanStack Query (never raw fetch/useEffect) - **State**: Zustand for global, React state for local - **Components**: shadcn/ui + Radix UI base - **Forms**: React Hook Form + Zod validation ## Critical Gotchas ### Never useEffect for Data Fetching ```typescript // ❌ BAD useEffect(() => { fetch('/api/data').then(setData); }, []); // ✅ GOOD const { data } = useQuery({ queryKey: ['data'], queryFn: fetchData, }); ``` ### Selective Memoization ```typescript // ✅ USE useMemo for: complex computations, large data transforms const filtered = useMemo(() => data.filter(x => x.status === 'active').map(transform), [data] ); // ✅ USE useCallback for: functions passed to children const handleClick = useCallback(() => doSomething(id), [id]); // ❌ DON'T memoize: simple values, primitives, local functions const name = data?.name ?? ''; // No useMemo needed ``` ### Zustand Selectors ```typescript // ✅ GOOD - specific selector const selectedEntity = useEntityStore(state => state.selectedEntity); // ❌ BAD - selecting entire store causes re-renders const { selectedEntity, filters } = useEntityStore(); ``` ### Browser Translation Safety (Google Translate) Many users auto-translate the page; the translator wraps text nodes in `<font>` elements, so React throws `NotFoundError: removeChild` when it reconciles a **bare dynamic text node** it re-parented. Wrap dynamic/conditional strings in their own element instead of rendering bare text. ```typescript // ❌ bare dynamic text → crash under translation <button>{icon}{label}</button> // ✅ wrap it → React swaps a stable element, stays translatable <button>{icon}<span>{label}</span></button> ``` For timer-driven text (typewriter/counter), also avoid per-tick `setState` — write into a ref'd node's `textContent` (React never reconciles it), or mark a decorative node `translate="no"`. Ref: [facebook/react#11538](https://github.com/facebook/react/issues/11538) (OPIK-7428, OPIK-7435). ## Layer Architecture ### Shared layers (used by all versions) `ui → shared` (one-way only) ### Per-version layers `ui → shared → v1/pages-shared → v1/pages` (one-way only) `ui → shared → v2/pages-shared → v2/pages` (one-way only) ### Module boundaries - v1/ CANNOT import from v2/ - v2/ CANNOT import from v1/ - `src/components/` is BLOCKED (old structure, no longer exists) - After modifying imports: `npm run deps:validate` ### Shared component rules - Backward-compatible changes only - Must not be version-aware (use `showProjectSelector={true}` not `isV2={true}`) - If behavior needs to change, create a new component instead ## State Location Decisions - **URL state**: filters, pagination, selected items - **Zustand**: user preferences, cross-component state - **React state**: form inputs, UI toggles ## Component Structure ```typescript const Component: React.FC<Props> = ({ prop }) => { // 1. State hooks // 2. Queries/mutations // 3. Memoization (only when needed) // 4. Event handlers if (isLoading) return <Loader />; if (error) return <ErrorComponent />; return <div>...</div>; }; ``` ## Query Patterns ```typescript // Query with params const { data } = useQuery({ queryKey: [ENTITY_KEY, params], queryFn: (context) => fetchEntity(context, params), }); // Mutation with invalidation const mutation = useMutation({ mutationFn: updateEntity, onSuccess: () => { queryClient.invalidateQueries({ queryKey: [ENTITY_KEY] }); }, }); ``` ## Reference Files - [forms.md](forms.md) - React Hook Form + Zod patterns - [ui-components.md](ui-components.md) - Button variants, typography, dark theme - [responsive-design.md](responsive-design.md) - Tailwind breakpoints vs useIsPhone - [testing.md](testing.md) - When to test, Vitest patterns - [code-quality.md](code-quality.md) - Lodash imports, naming, deps:validate - [performance.md](performance.md) - Bundle optimization, rendering, memoization - [permissions.md](permissions.md) - `usePermissions()` guard guidance for UI actions
Auf GitHub ansehen