← Back
Edit Post
Title
Description
Server vs Client Components, CSS :has() for parent styling, data-pending attribute pattern.
Content
Markdown supported
# React Server Components Server Components render on the server, can be `async`, and fetch data directly. ## Example: BlogList From `app/page.tsx`: ```tsx async function BlogList() { const posts = await getPublishedPosts(); return posts.map(post => <Card key={post.slug}>{post.title}</Card>); } ``` ## When to Use Client Components Add `'use client'` when you need interactivity—event handlers or React hooks. From `app/dashboard/_components/ArchiveButton.tsx`: ```tsx 'use client'; export function ArchiveButton({ slug, archived }) { const [optimisticArchived, setOptimisticArchived] = useOptimistic(archived); const isPending = optimisticArchived !== archived; return ( <form data-pending={isPending || undefined} action={async () => { let newValue; setOptimisticArchived(current => { newValue = !current; return newValue; }); await toggleArchivePost(slug, newValue); }} > <button>{optimisticArchived ? 'Unarchive' : 'Archive'}</button> </form> ); } ``` ## Example: Composition with CSS :has() Server Components render Client Components. Use CSS `:has()` to style parent elements based on child state—no state lifting required: ```tsx // PostList.tsx (Server Component) export async function PostList({ searchParams }) { const posts = await getPosts(validFilter); return posts.map(post => ( <Card className="has-data-pending:animate-pulse has-data-pending:bg-muted/70"> <ArchiveButton slug={post.slug} archived={post.archived} /> </Card> )); } ``` The `has-data-pending:` variant (Tailwind's `:has([data-pending])`) lets the Card react to the button's pending state without becoming a Client Component. Keep Client Components at the leaves to maximize server rendering.
Published
Save Changes
Cancel