Summary
Several components in @vllnt/ui 0.2.x use React hooks (useState, useEffect, useRef, etc.) but the source files lack the 'use client' directive. When a Next.js App Router consumer imports them from the barrel (@vllnt/ui), Next's bundler tries to render them server-side and crashes with:
TypeError: c.useState is not a function or its return value is not iterable
Reproduction
In a Next.js 16 App Router app (React 19):
// app/page.tsx (Server Component)
import { AnimatedText } from '@vllnt/ui'
export default function Page() {
return <AnimatedText text="hello" variant="matrix" />
}
Result: 500 Application Error on page load. Vercel log:
TypeError: c.useState is not a function or its return value is not iterable
at .next/server/chunks/ssr/app_page_tsx_*.js
digest: '...'
Wrapping in a local 'use client' file does not help because the barrel re-export from packages/ui/src/index.ts does not carry the directive to consumers; Next's RSC bundler treats the imported component as a server component regardless.
Current workaround is next/dynamic({ ssr: false }), which defeats SSR and worsens LCP.
Affected files (confirmed)
Files using useState/useEffect/useRef/useCallback/useMemo without 'use client' at top:
packages/ui/src/components/animated-text/animated-text.tsx
packages/ui/src/components/usage-breakdown/usage-breakdown.tsx
packages/ui/src/components/chart/line-chart.tsx
packages/ui/src/components/chart/area-chart.tsx
packages/ui/src/components/data-list/data-list.tsx
packages/ui/src/components/spinner/unicode-spinner.tsx
packages/ui/src/components/activity-log/activity-log.tsx
Likely also affected (use animation/interactivity hooks, need audit):
ai-streaming-text, ai-chat-input, ai-message-bubble
border-beam, marquee, number-ticker, ticker-tape
live-feed, countdown-timer, status-board, status-indicator
Fix
Add 'use client' as the first line of each affected source file:
'use client'
import { useState } from 'react'
// ... rest of component
Components that are pure (no hooks, no browser APIs) can stay as server components — e.g. Badge, Card, Separator, Breadcrumb.
Automated audit command
To keep this from regressing, add a CI check:
# Fail if any .tsx file uses React hooks without a 'use client' directive
files=$(grep -rL "^'use client'" packages/ui/src/components --include="*.tsx" | \
xargs -I{} grep -l "useState\|useEffect\|useRef\|useLayoutEffect\|useCallback\|useMemo\|useReducer" {} 2>/dev/null | \
grep -v "\.stories\.tsx")
if [ -n "$files" ]; then
echo "Missing 'use client' directive:"
echo "$files"
exit 1
fi
Consumer-side workaround (for reference)
Until fixed:
'use client'
import dynamic from 'next/dynamic'
const AnimatedText = dynamic(
() => import('@vllnt/ui').then((m) => ({ default: m.AnimatedText })),
{ ssr: false }
)
Found while upgrading vllnt.ai to @vllnt/ui@0.2.1. Happy to open a PR with the directive additions if you'd like.
Summary
Several components in
@vllnt/ui0.2.x use React hooks (useState,useEffect,useRef, etc.) but the source files lack the'use client'directive. When a Next.js App Router consumer imports them from the barrel (@vllnt/ui), Next's bundler tries to render them server-side and crashes with:Reproduction
In a Next.js 16 App Router app (React 19):
Result: 500 Application Error on page load. Vercel log:
Wrapping in a local
'use client'file does not help because the barrel re-export frompackages/ui/src/index.tsdoes not carry the directive to consumers; Next's RSC bundler treats the imported component as a server component regardless.Current workaround is
next/dynamic({ ssr: false }), which defeats SSR and worsens LCP.Affected files (confirmed)
Files using
useState/useEffect/useRef/useCallback/useMemowithout'use client'at top:packages/ui/src/components/animated-text/animated-text.tsxpackages/ui/src/components/usage-breakdown/usage-breakdown.tsxpackages/ui/src/components/chart/line-chart.tsxpackages/ui/src/components/chart/area-chart.tsxpackages/ui/src/components/data-list/data-list.tsxpackages/ui/src/components/spinner/unicode-spinner.tsxpackages/ui/src/components/activity-log/activity-log.tsxLikely also affected (use animation/interactivity hooks, need audit):
ai-streaming-text,ai-chat-input,ai-message-bubbleborder-beam,marquee,number-ticker,ticker-tapelive-feed,countdown-timer,status-board,status-indicatorFix
Add
'use client'as the first line of each affected source file:Components that are pure (no hooks, no browser APIs) can stay as server components — e.g.
Badge,Card,Separator,Breadcrumb.Automated audit command
To keep this from regressing, add a CI check:
Consumer-side workaround (for reference)
Until fixed:
Found while upgrading vllnt.ai to
@vllnt/ui@0.2.1. Happy to open a PR with the directive additions if you'd like.