smithery.ai

opik-frontend

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

First seen Apr 13, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,282 B
  • docs SUMMARY.md 134 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

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

// ❌ BAD
useEffect(() => {
  fetch('/api/data').then(setData);
}, []);

// ✅ GOOD
const { data } = useQuery({
  queryKey: ['data'],
  queryFn: fetchData,
});

Selective Memoization

// ✅ 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

// ✅ 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.

// ❌ 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 (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

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

// 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