This content originally appeared on DEV Community and was authored by sudip khatiwada
When working with Next.js 13+ and the App Router, understanding the difference between Server Components and Client Components is crucial for building efficient web applications. Sometimes, you’ll need to convert a Server Component into a Client Component. Let’s explore when, why, and how to do this properly.
What Are Server Components vs Client Components?
Server Components (Default)
- Run on the server during build time or request time
- Cannot use browser-specific features like
useState
,useEffect
, or event handlers - Great for fetching data and rendering static content
- Smaller JavaScript bundle size
Client Components
- Run in the browser after the page loads
- Can use React hooks and browser APIs
- Handle user interactions like clicks, form inputs
- Require the
"use client"
directive at the top
When Do You Need Client Components?
You need to convert to Client Components when you want to:
Use React hooks (
useState
, useEffect
, etc.)
Handle user interactions (onClick, onSubmit)
Access browser APIs (localStorage, window object)
Use event listeners
Create interactive UI elements
Simple Code Example: Converting Server to Client Component
Let’s see a practical example of converting a Server Component to a Client Component.
Before: Server Component (Won’t Work for Interactions)
// app/components/Counter.jsx
// This is a Server Component by default
export default function Counter() {
// ❌ This won't work in Server Components
// const [count, setCount] = useState(0);
return (
<div>
<h2>Counter: 0</h2>
{/* ❌ onClick won't work in Server Components */}
<button>Click me</button>
</div>
);
}
After: Client Component (Interactive Version)
// app/components/Counter.jsx
"use client"; // ✅ This makes it a Client Component
import { useState } from 'react';
export default function Counter() {
// ✅ Now we can use useState
const [count, setCount] = useState(0);
// ✅ Event handlers work in Client Components
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<h2>Counter: {count}</h2>
{/* ✅ onClick works perfectly now */}
<button onClick={handleClick}>
Click me to increase!
</button>
</div>
);
}
Using the Component in Your Page
// app/page.jsx
import Counter from './components/Counter';
export default function HomePage() {
return (
<div>
<h1>My Next.js App</h1>
<Counter />
</div>
);
}
Step-by-Step Conversion Process
Step 1: Add “use client” Directive
Add "use client";
at the very top of your component file.
Step 2: Import Required Hooks
Import any React hooks you need from ‘react’.
Step 3: Add Interactive Logic
Now you can add state, event handlers, and other interactive features.
Real-World Example: Contact Form
Here’s a more practical example showing a contact form conversion:
Server Component (Static Form)
// app/components/ContactForm.jsx
export default function ContactForm() {
return (
<form>
<input type="text" placeholder="Your Name" />
<input type="email" placeholder="Your Email" />
<textarea placeholder="Your Message"></textarea>
<button type="submit">Send Message</button>
</form>
);
}
Client Component (Interactive Form)
// app/components/ContactForm.jsx
"use client";
import { useState } from 'react';
export default function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted:', formData);
// Handle form submission logic here
};
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Your Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Your Email"
/>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
placeholder="Your Message"
></textarea>
<button type="submit">Send Message</button>
</form>
);
}
Best Practices and Tips
Do This:
- Keep Server Components as the default when possible
- Only convert to Client Components when you need interactivity
- Use Client Components for forms, buttons, and dynamic UI
- Place “use client” at the top of the file
Avoid This:
- Don’t make everything a Client Component
- Don’t forget the “use client” directive when needed
- Don’t try to use hooks in Server Components
Performance Considerations
Server Components Benefits:
- Faster initial page load
- Better SEO
- Smaller JavaScript bundle
- Server-side data fetching
Client Components Trade-offs:
- Larger bundle size
- Runs in browser (uses client resources)
- Better for interactivity and user experience
Common Errors and Solutions
Error: “You’re importing a component that needs useState”
Solution: Add "use client"
directive to the component.
Error: “Event handlers cannot be passed to Server Components”
Solution: Convert the component to a Client Component with "use client"
.
Conclusion
Converting Server Components to Client Components in Next.js is straightforward once you understand the key differences. Remember:
- Server Components are great for static content and data fetching
- Client Components are essential for interactivity and user interactions
- Add
"use client"
at the top when you need browser features - Use the right component type for the right job
By following these guidelines and examples, you’ll be able to build efficient, interactive Next.js applications that provide the best user experience while maintaining optimal performance.
Ready to build your next interactive Next.js application? Start with Server Components by default and convert to Client Components only when you need that extra interactivity!
This content originally appeared on DEV Community and was authored by sudip khatiwada