Abhishek Yadav
@h6165
Isn't it already there in Next.js ? The SSR thing ?
SSR - we render the component on server side, usually with some data. The component is then *sent* to the browser where it becomes interactive (hydration)
Server Component - component rendered on server. Output (not component) sent to browser
Server Component
Server Component - can't be interactive - can't useState, can't use the context API
//
// Next.js: components in the 'app' directory
//
import { heavyFunction } from 'heavy-library';
export function MyComponent() {
const result = heavyFunction()
return (
<div>
{ result }
</div>
);
}
No code from 'heavy-library' reaches the browser
'use client'; // Next.js: declare it's a client component
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
// Next.js
// client component called within server component
//
import { heavyFunction } from 'heavy-library';
import { Counter as CounterOnClient } from './counter'
export function MyServerComponent() {
const result = heavyFunction()
return (
<div>
{ result }
<CounterOnClient />
</div>
);
}
Call Client component from the server component
*conditions apply
The project status
The Devil in the details
Devil in details: Server -> Client props
// Next.js
// client component called within server component
// with props
import { Counter as CounterOnClient } from './counter'
export function MyServerComponent() {
const x = "hello"
const fn = ()=>{}
return (
<>
<CounterOnClient prop1={x} /> // Ok
<CounterOnClient prop1={fn} /> // NOT OK
</>
);
}
Devil in details: Importing Server Component in Client
'use client';
import MyServerComponent from './ServerComponent'; // Fails here
export function CounterOnClient() {
return (
<>
<MyServerComponent />
// other stuff
</>
);
}
Devil in details: Importing Server Component in Client
'use client';
// Server Component called as children
function CounterLayout({ children }) {
return (
<>
{ children }
// other stuff
</>
);
}
// Next.js
// Using server components
// inside of client component
//
import MyServerComponent from './ServerComponent';
import { CounterLayout } from './counterLayout'
export function MainServerComponent() {
return (
<CounterLayout>
<MyServerComponent />
</CounterLayout>
);
}
Devil in details: Preventing poisoning
'use client';
// This is legal
import { myServerOnlyFn } from './utils'
function CounterLayout() {
const value = myServerOnlyFn()
return (
<>
{ value }
// other stuff
</>
);
}
//
// A function that should be called
// on server side only
//
export function myServerOnlyFn() {
// My
// Super
// secret
// algorithm
return Math.random();
}
Devil in details: Preventing poisoning
'use client';
// This fails now
import { myServerOnlyFn } from './utils'
function CounterLayout() {
const value = myServerOnlyFn()
return (
<>
{ value }
// other stuff
</>
);
}
import "server-only"; // NPM package
//
// A function that should be called
// on server side only
//
export function myServerOnlyFn() {
// My
// Super
// secret
// algorithm
return Math.random();
}
Third party component libraries may fail in server components, because they may be interactive (useState)
If they have the 'use-client' declaration, then its okay
We can write wrapper components for such situation
Devil in details: Using third party components
// Next.js
import { SliderComponent } from 'slider-lib'
export function ServerComponent() {
return (
// Fails
<SliderComponent />
);
}
Devil in details: Using third party components
// Next.js
import { MySliderComponent } from './sliderWrapper'
export function ServerComponent() {
return (
// Works
<MySliderComponent />
);
}
//
// Wrapper component
//
'use-client'
import { SliderComponent } from 'slider-lib'
export function MySliderComponent() {
return (
<SliderComponent />
);
}
The wrapper
Devil in details: Fetch de-duplication
Next.js recommendations:
fetch data in server components, not client
fetch in each (server) component that needs it.
Don't: fetch it all at once in a parent, and passing down as props. (Context is not available anyway)
Next.js performs a de-duplication on these requests
Benefit: reduced coupling
That's all