Developers building with Next.js 13+ App Router often write Server Components that fetch data directly from a database or API. When using GitHub Copilot, the generated code sometimes defaults to Client Components with hooks like useEffect or useState, which defeats the server-first architecture. This mismatch happens because Copilot’s training data includes many React patterns that assume client-side rendering. This article explains how to guide Copilot to produce correct Server Component patterns for the App Router, including data fetching, error handling, and component composition.
Key Takeaways: Server Component Patterns with GitHub Copilot
- File naming convention
page.tsxandlayout.tsx: Files inside theapp/directory are Server Components by default. Copilot respects this when you open the file first. 'use server'directive for Server Actions: Place this at the top of a separate file or inline function to tell Copilot you want server-side mutation logic.- Inline comment
// Server Component: Adding this comment at the top of a file reduces the chance Copilot suggestsuseStateoruseEffect.
Why Copilot Defaults to Client Components in Next.js
GitHub Copilot learns from billions of lines of public code. The majority of React examples on GitHub use Client Components with hooks. When you start typing in a .tsx file, Copilot does not automatically know whether the file is a Server Component or a Client Component. Without explicit hints, it suggests patterns like useEffect to fetch data or useState to manage local state. These patterns work but break the App Router’s server-first model, which prefers async components that fetch data directly.
The App Router treats every component inside the app/ directory as a Server Component unless the file contains the 'use client' directive. Copilot can detect this directive and adjust its suggestions accordingly. However, it does not infer the directory context from the file path alone. You must provide clear signals through file structure, comments, or the first few lines you type.
Patterns to Guide Copilot for Server Components
The following patterns help Copilot produce correct Server Component code. Apply them consistently across your project.
1. Start with an Async Function Signature
Server Components in the App Router are async functions. When you type export default async function Page(), Copilot immediately recognizes the server context. It will avoid suggesting hooks and instead propose await calls for data fetching.
- Open a new file in the
app/directory
Create a file likeapp/products/page.tsx. Do not add'use client'at the top. - Type the async function signature
Writeexport default async function ProductsPage() {on the first line. Press Enter. - Accept the data-fetching suggestion
Copilot will likely suggestconst products = await db.product.findMany()or similar. Press Tab to accept. - Add the JSX return
Typereturn (and let Copilot suggest a map over the products array. It will avoiduseEffectbecause the function is async.
2. Use Inline Comments to Declare Context
A simple comment at the top of the file tells Copilot the environment. This works even if you have not typed the function signature yet.
- Add a context comment
Type// Server Component — noon the first line.'use client'directive - Write the component name
Typeexport default function ProductList(). Copilot will now avoiduseStateanduseEffectbecause the comment signals a server context. - Fetch data directly
Copilot will suggestconst data = await fetchData()or a database call. Accept and wrap in JSX.
3. Define Server Actions in Separate Files
Server Actions are functions that run on the server but are called from Client Components. Copilot often confuses them with API routes. To get correct suggestions, define actions in a file with the 'use server' directive at the top.
- Create a server actions file
Make a file likeapp/actions.tsorlib/actions.ts. Add'use server'as the first line. - Write an async function
Typeexport async function createProduct(formData: FormData) {. Copilot will suggest server-side logic likeconst name = formData.get('name')and database insert. - Import the action in a Client Component
In a'use client'file, import the action and pass it to a form. Copilot will suggestcorrectly.
Common Mistakes When Copilot Generates Server Components
Copilot Adds useEffect Inside a Server Component
If you start a file without an async signature or context comment, Copilot may suggest useEffect for data fetching. This causes a build error because hooks are not allowed in Server Components. To fix it, delete the hook and replace it with an await call inside the component body. Then add the async keyword to the function.
Copilot Suggests 'use client' Unnecessarily
Sometimes Copilot adds 'use client' at the top of a file that only renders static data. This forces the component to become a Client Component, losing the performance benefits of server rendering. Remove the directive and ensure the component does not use any hooks or browser APIs. If you need interactivity, keep the directive and keep the component small.
Server Action Code Appears Inside a Client Component
Copilot may suggest writing a server action function directly inside a 'use client' file. This does not work because the function runs on the client. Move the action to a separate file with 'use server' and import it. Then delete the inline function from the Client Component.
Server Component vs Client Component: Key Differences
| Item | Server Component | Client Component |
|---|---|---|
| Default in App Router | Yes (files in app/ without 'use client') |
No (requires 'use client' directive) |
| Data fetching | Direct await inside async component |
useEffect or third-party library like React Query |
| Hooks allowed | No (useState, useEffect cause errors) |
Yes (all React hooks) |
| Copilot hint needed | Async signature or // Server Component comment |
'use client' directive on first line |
| Bundle size impact | Zero JavaScript sent to the client | Full component JavaScript sent to the client |
You can now write Server Components with GitHub Copilot that follow the App Router pattern correctly. Start every new file with an async function signature or a context comment to avoid hook suggestions. For Server Actions, always use a separate file with the 'use server' directive. As a next step, try Copilot’s inline chat feature to refactor an existing Client Component into a Server Component by typing /fix and describing the change.