OP

opik-frontend

Provides architectural guidelines and best practices for the Opik React frontend codebase.

Install

mkdir -p .claude/skills/opik-frontend && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/814" && unzip -o skill.zip -d .claude/skills/opik-frontend && rm skill.zip

Installs to .claude/skills/opik-frontend

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

React frontend patterns for Opik. Use when working in apps/opik-frontend, on components, state, or data fetching.
113 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Fetch data using TanStack Query
  • Build components with shadcn/ui and Radix UI
  • Validate forms with React Hook Form and Zod
  • Apply selective memoization with useMemo and useCallback

How it works

The skill enforces specific patterns for data fetching using TanStack Query, state management with Zustand, and component creation with shadcn/ui, ensuring consistency and performance.

Inputs & outputs

You give it
React component logic for data fetching, state management, or UI
You get back
Standardized, performant React frontend code for Opik

When to use opik-frontend

  • Implementing efficient data fetching
  • Managing global state with Zustand
  • Applying consistent component architecture
  • Refactoring legacy useEffect fetching

About this skill

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

When not to use it

  • For simple values or primitives that do not require memoization
  • For local functions that are not passed to children components
  • When importing from different version layers (e.g., v1 from v2)

Limitations

  • Cannot use raw fetch or useEffect for data fetching
  • Cannot import from `src/components/`
  • Shared components must be backward-compatible and not version-aware

How it compares

This skill provides a defined architectural framework for Opik's frontend development, preventing ad-hoc implementations like raw useEffect for data fetching and promoting selective memoization.

Compared to similar skills

opik-frontend side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
opik-frontend (this skill)32moNo flagsIntermediate
accessibility-compliance452moNo flagsIntermediate
frontend-code-review112moNo flagsAdvanced
feature-flags66moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry