Next.js course · Module 1: Next.js Configuration

Component System - Client and Server Components

13 min read
In this lesson10

In Quantum Metropolis, quantum system architects always have to make fundamental decisions: which processes to run on central quantum processors (for tasks requiring the highest computational power), and which on citizens' end devices (for instant interactivity). This dichotomy perfectly reflects the revolutionary change that the App Router introduced in Next.js 13 and that Next.js 16 develops further - the division into Server Components and Client Components.

The React Server Components Revolution

React Server Components (RSC) represent the biggest paradigm shift in React since the introduction of Hooks. Before their appearance, all React components were rendered on the server or client as a cohesive whole. Now we can create applications where some components are rendered exclusively on the server, others on the client, and data and UI structures flow seamlessly between them.

It's like the difference between traditional transport systems in Quantum Metropolis, where vehicles move only along specific routes, and the new quantum transport system, where matter can be teleported directly to its destination.

Server Components - Basics

In the App Router of Next.js 16, all components are Server Components by default, unless we explicitly mark them as Client Components.

Server Components are rendered on the server and don't send any JavaScript to the browser. It's like the quantum computing processors of Quantum Metropolis, which perform complex calculations and transmit only the final results to citizens' devices.

Advantages of Server Components:

  1. Access to server resources - direct access to databases, file system, and other server resources
  2. Security - sensitive data and logic remain on the server
  3. Automatic code splitting - only necessary code is sent to the client
  4. Smaller JavaScript bundle size - no need to send rendering code
  5. Better SEO - faster static rendering
  6. Better first load performance - less JavaScript to download and execute

Server Component Example:

1// app/dashboard/stats/page.tsx - Server Component (by default)
2export default async function StatsPage() {
3  // This code runs ONLY on the server
4  const stats = await fetchQuantumSystemStats();
5
6  return (
7    <section className="quantum-stats">
8      <h1>Quantum Systems Statistics</h1>
9      <div className="stats-grid">
10        {stats.map(stat => (
11          <StatCard key={stat.id} name={stat.name} value={stat.value} />
12        ))}
13      </div>
14    </section>
15  );
16}

Note several important aspects:

  1. Using async/await directly in the component - this is only possible in Server Components
  2. No use of React hooks (useState, useEffect, etc.) - they are not available in Server Components
  3. No event handling - Server Components cannot handle user events

Client Components - Basics

Client Components provide interactivity: on the first visit Next.js pre-renders them to HTML on the server, then they are hydrated in the browser and keep running on the client. To mark a component as a Client Component, we add the "use client" directive at the top of the file.

This process is called hydration: in the browser, React attaches event handlers to the HTML that was rendered on the server earlier. Only after hydration do buttons respond to clicks, and useEffect effects run after it.

It's like the personal quantum devices of Quantum Metropolis citizens - lighter, but instantly responsive to user actions.

Advantages of Client Components:

  1. Interactivity - handling user events (clicks, text input, etc.)
  2. React hooks - ability to use useState, useEffect, useContext, and other hooks
  3. Access to browser APIs - window, document, localStorage, etc.
  4. Client-side effects - animations, real-time interactions

Client Component Example:

1// app/dashboard/interactive-controls.tsx
2"use client"; // This directive marks the client component
3
4import { useState, useEffect } from 'react';
5
6export default function InteractiveControls() {
7  // This code runs in the user's browser
8  const [activeSystem, setActiveSystem] = useState('energy');
9  const [status, setStatus] = useState('idle');
10
11  useEffect(() => {
12    // Effects run only on the client
13    const timer = setTimeout(() => {
14      setStatus('ready');
15    }, 1000);
16
17    return () => clearTimeout(timer);
18  }, []);
19
20  return (
21    <div className="control-panel">
22      <h2>System: {activeSystem}</h2>
23      <p>Status: {status}</p>
24      <div className="button-group">
25        <button onClick={() => setActiveSystem('transport')}>
26          Transport
27        </button>
28        <button onClick={() => setActiveSystem('energy')}>
29          Energy
30        </button>
31        <button onClick={() => setActiveSystem('security')}>
32          Security
33        </button>
34      </div>
35    </div>
36  );
37}

Key aspects:

  1. The "use client" directive at the beginning of the file
  2. Using React hooks (useState, useEffect)
  3. Event handling (onClick)

Combining Server and Client Components

The most powerful feature of the new model is the ability to combine Server and Client Components in a single application. Server Components can render Client Components, but not the other way around (with some exceptions using patterns). It's like the hybrid transport system of Quantum Metropolis, where main routes are served by quantum teleportation, and the last mile by personal vehicles.

Example of combining components:

1// app/dashboard/page.tsx - Server Component
2export default async function DashboardPage() {
3  // This code runs ONLY on the server
4  const dashboardData = await fetchDashboardData();
5
6  return (
7    <main className="quantum-dashboard">
8      <h1>Quantum Metropolis Control Panel</h1>
9
10      {/* Server Component */}
11      <SystemStatusPanel systemsData={dashboardData.systems} />
12
13      {/* Client Component (imported and used in Server Component) */}
14      <InteractiveControls />
15    </main>
16  );
17}
1// app/dashboard/system-status-panel.tsx - Server Component (by default)
2export default function SystemStatusPanel({ systemsData }) {
3  return (
4    <div className="system-status-panel">
5      <h2>System Status</h2>
6      <div className="status-grid">
7        {systemsData.map(system => (
8          <SystemStatusCard key={system.id} system={system} />
9        ))}
10      </div>
11    </div>
12  );
13}
1// app/dashboard/system-status-card.tsx - Server Component (by default)
2import { StatusIndicator } from './status-indicator';
3
4export default function SystemStatusCard({ system }) {
5  return (
6    <div className="status-card">
7      <h3>{system.name}</h3>
8      <p>Performance: {system.performance}%</p>
9
10      {/* Client Component used in Server Component */}
11      <StatusIndicator status={system.status} />
12    </div>
13  );
14}
1// app/dashboard/status-indicator.tsx - Client Component
2"use client";
3
4import { useState, useEffect } from 'react';
5
6export function StatusIndicator({ status }) {
7  const [blinking, setBlinking] = useState(false);
8
9  useEffect(() => {
10    const interval = setInterval(() => {
11      setBlinking(prev => !prev);
12    }, 500);
13
14    return () => clearInterval(interval);
15  }, []);
16
17  return (
18    <div className={`status-indicator ${status} ${blinking ? 'blink' : ''}`}>
19      {status === 'active' ? 'Active' : 'Inactive'}
20    </div>
21  );
22}

Optimization Strategies When Designing Components

Properly deciding which components should be Server Components and which should be Client Components is crucial for application performance. Here are some strategies to help you make these decisions:

1. Server Component as the Default Choice

Always start with Server Components unless you need features available only in Client Components. This is similar to the approach in Quantum Metropolis, where central computing units are used by default unless instant interactivity is needed.

2. Granular Interactivity

Instead of marking an entire large component as "use client", extract only the interactive parts into separate Client Components, leaving the rest as Server Components.

Before optimization:

1// LESS OPTIMAL - entire component is a Client Component
2"use client";
3import { useState } from 'react';
4
5export default function ControlPanel() {
6  const [activeTab, setActiveTab] = useState('overview');
7
8  return (
9    <div className="control-panel">
10      <h2>Control Panel</h2>
11      <p>This panel allows you to manage quantum systems.</p>
12
13      <div className="tabs">
14        <button onClick={() => setActiveTab('overview')}>Overview</button>
15        <button onClick={() => setActiveTab('settings')}>Settings</button>
16      </div>
17
18      <div className="tab-content">
19        {activeTab === 'overview' ? (
20          <OverviewContent /> // Lots of static content
21        ) : (
22          <SettingsContent /> // Lots of static content
23        )}
24      </div>
25    </div>
26  );
27}

After optimization:

1// BETTER - split into Client and Server Components
2
3// control-panel.tsx - Server Component
4import { TabController } from './tab-controller';
5import { OverviewContent } from './overview-content';
6import { SettingsContent } from './settings-content';
7
8export default function ControlPanel() {
9  return (
10    <div className="control-panel">
11      <h2>Control Panel</h2>
12      <p>This panel allows you to manage quantum systems.</p>
13
14      {/* Only TabController is a Client Component; the tab contents stay on the server */}
15      <TabController overview={<OverviewContent />} settings={<SettingsContent />} />
16    </div>
17  );
18}
19
20// tab-controller.tsx - Client Component
21"use client";
22import { useState, type ReactNode } from 'react';
23
24export function TabController({ overview, settings }: { overview: ReactNode; settings: ReactNode }) {
25  const [activeTab, setActiveTab] = useState('overview');
26
27  return (
28    <>
29      <div className="tabs">
30        <button onClick={() => setActiveTab('overview')}>Overview</button>
31        <button onClick={() => setActiveTab('settings')}>Settings</button>
32      </div>
33
34      <div className="tab-content">
35        {activeTab === 'overview' ? overview : settings}
36      </div>
37    </>
38  );
39}

A component imported in a file with "use client" becomes a Client Component too. That is why the Server Component ControlPanel imports OverviewContent and SettingsContent and passes them to TabController as props, just like children.

3. "Container and Presentation" Pattern

Use Server Components as "containers" for fetching data and organization, and Client Components as "presentation components" responsible for interactivity.

1// products-container.tsx - Server Component (container)
2export default async function ProductsContainer() {
3  const products = await fetchProducts();
4
5  return <ProductsList products={products} />;
6}
7
8// products-list.tsx - Client Component (presentation)
9"use client";
10import { useState } from 'react';
11
12export function ProductsList({ products }) {
13  const [sortBy, setSortBy] = useState('name');
14  const sortedProducts = [...products].sort((a, b) =>
15    sortBy === 'price' ? a.price - b.price : a.name.localeCompare(b.name)
16  );
17
18  return (
19    <div>
20      <div className="sorting">
21        <button onClick={() => setSortBy('name')}>Sort by name</button>
22        <button onClick={() => setSortBy('price')}>Sort by price</button>
23      </div>
24
25      <ul className="products">
26        {sortedProducts.map(product => (
27          <li key={product.id}>{product.name} - {product.price}</li>
28        ))}
29      </ul>
30    </div>
31  );
32}

Data Flow Between Server and Client Components

Passing data between server and client requires a special approach. In the RSC model, props flow down the component tree from server to client; in the other direction the client sends data by calling Server Actions (you will meet them in a moment). It's like quantum channels in Quantum Metropolis - orders flow from the central hub to personal devices, and reports come back through a separate channel.

Passing Data to Client Components:

1// server-component.tsx - Server Component
2async function ServerComponent() {
3  const data = await fetchSensitiveData();
4
5  // Pass processed data to client
6  return <ClientComponent processedData={data.publicPart} />;
7}
8
9// client-component.tsx - Client Component
10"use client";
11
12export function ClientComponent({ processedData }) {
13  // Using data passed from the server
14  return <div>{processedData.map(item => <p key={item.id}>{item.name}</p>)}</div>;
15}

Handling User Actions from Client Components:

When a Client Component needs to perform an action that involves the server, we use Server Actions or Route Handlers (route.ts files; in the Pages Router their counterpart is API Routes).

Using Server Actions (stable since Next.js 14):

1// actions.js - Server Actions
2"use server";
3
4export async function updateSystemStatus(systemId, newStatus) {
5  // This function runs on the server
6  await database.systems.update(systemId, { status: newStatus });
7  return { success: true };
8}
9
10// client-component.tsx - Client Component
11"use client";
12import { useState } from 'react';
13import { updateSystemStatus } from './actions';
14
15export function StatusToggle({ systemId, initialStatus }) {
16  const [status, setStatus] = useState(initialStatus);
17
18  async function handleToggle() {
19    const newStatus = status === 'active' ? 'inactive' : 'active';
20    const result = await updateSystemStatus(systemId, newStatus);
21
22    if (result.success) {
23      setStatus(newStatus);
24    }
25  }
26
27  return (
28    <button onClick={handleToggle}>
29      {status === 'active' ? 'Deactivate' : 'Activate'}
30    </button>
31  );
32}

Differences in Reloading and Navigation

In the traditional React model, page reloading or navigation often meant restarting the entire application. In the React Server Components model, navigation is more optimal:

  • Server Components are re-rendered on the server during navigation
  • Client Components in shared layouts keep their state during navigation; components of the page you leave are unmounted by default
  • Page transitions are smoother thanks to the "instant navigation" mechanism

It's like the advanced quantum switches in Quantum Metropolis - they allow instant transitions between different layouts without interrupting system operations.

Common Mistakes and Pitfalls

1. Using React hooks in Server Components

1// WRONG - Server Component trying to use React hooks
2export default function ServerComponent() {
3  // This will cause an error!
4  const [count, setCount] = useState(0);
5
6  return <div>Count: {count}</div>;
7}

2. Attempting to access browser APIs in Server Components

1// WRONG - Server Component trying to use browser APIs
2export default function ServerComponent() {
3  // This will cause an error! (ReferenceError: window is not defined)
4  const windowWidth = window.innerWidth;
5
6  return <div>Width: {windowWidth}px</div>;
7}

3. Passing functions from Server Components to Client Components

1// WRONG - Server Component passes a regular function to a Client Component
2// app/page.tsx (Server Component)
3import ClientButton from './client-button';
4
5export default function Page() {
6  const handleClick = () => console.log("Clicked!");
7
8  // This won't work! Props passed to a Client Component must be serializable
9  return <ClientButton onClick={handleClick} />;
10}

Server Actions are the exception: you can pass a function marked with "use server" to a Client Component.

Advanced Patterns for Using Server and Client Components

1. Provider Pattern

If you need to share state between different Client Components, use a Client Provider at a high level. This is similar to central synchronization units in Quantum Metropolis, which coordinate the actions of different personal devices.

1// providers.tsx - Client Component
2"use client";
3import { createContext, useState } from 'react';
4
5export const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });
6
7export function ThemeProvider({ children }) {
8  const [theme, setTheme] = useState('light');
9  const toggleTheme = () => setTheme(prev => prev === 'light' ? 'dark' : 'light');
10
11  return (
12    <ThemeContext.Provider value={{ theme, toggleTheme }}>
13      {children}
14    </ThemeContext.Provider>
15  );
16}
1// layout.tsx - Server Component
2import { ThemeProvider } from './providers';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5  return (
6    <html>
7      <body>
8        <ThemeProvider>
9          {children}
10        </ThemeProvider>
11      </body>
12    </html>
13  );
14}

2. "Lifting State Up" Pattern

When you have several Client Components that need access to the same state, lift the state to a common Client Component ancestor, not to a Server Component.

1// parent.tsx - Server Component
2export default function ParentServerComponent() {
3  return <ClientParent />;
4}
5
6// client-parent.tsx - Client Component (common ancestor)
7"use client";
8import { useState } from 'react';
9
10export function ClientParent() {
11  const [sharedState, setSharedState] = useState('initial');
12
13  return (
14    <div>
15      <ClientChildA sharedState={sharedState} setSharedState={setSharedState} />
16      <ClientChildB sharedState={sharedState} />
17    </div>
18  );
19}

3. Strategic Use of Suspense

Use the Suspense component to manage loading states in a hybrid application:

1// page.tsx - Server Component
2import { Suspense } from 'react';
3
4export default function Page() {
5  return (
6    <div>
7      <h1>Quantum Metropolis System Data</h1>
8
9      <Suspense fallback={<div>Loading data...</div>}>
10        <AsyncDataComponent />
11      </Suspense>
12    </div>
13  );
14}
15
16// async-data-component.tsx - Server Component with asynchronous code
17async function AsyncDataComponent() {
18  // This component can be suspended during loading
19  const data = await fetchData();
20
21  return <div>{/* Rendering data */}</div>;
22}

Summary

The component system in Next.js 16 based on React Server Components is a revolutionary change that combines the advantages of server rendering with client-side interactivity. Just as in Quantum Metropolis, where quantum teleportation and personal vehicles create a hybrid transport system, in Next.js server and client components work together, creating an efficient, scalable, and user-friendly interface.

When designing Next.js 16 applications:

  1. Use Server Components by default for better performance and security
  2. Switch to Client Components only when you need interactivity, hooks, or browser APIs
  3. Split components granularly to minimize the amount of JavaScript sent to the client
  4. Remember the direction of props - from server to client (you send data the other way with Server Actions)
  5. Use Server Actions for form processing and other user actions

Proper use of Server and Client Components will allow you to create next-generation Next.js applications that combine fast loading, SEO performance, and rich user interfaces - without the compromises that were necessary in traditional React applications.

In the next lesson, we will move to practice and create our first "Hello World" application using Next.js 16 and App Router, where we'll apply the knowledge gained about Server and Client Components.

Code for this lesson: App.tsx
1// Hooks - useState and useEffect - Metropolis Quantum
2import React, { useState, useEffect } from 'react';
3
4console.log("React Hooks - useState and useEffect");
5console.log("Quantum Systems Management\n");
6
7// ==========================================
8// 1. useState - State Management
9// ==========================================
10
11function QuantumReactor() {
12  const [temperature, setTemperature] = useState<number>(20);
13  const [pressure, setPressure] = useState<number>(100);
14  const [status, setStatus] = useState<'stable' | 'warning' | 'critical'>('stable');
15
16  // useEffect - monitor temperature and pressure changes
17  useEffect(() => {
18    console.log(`Temperature: ${temperature}°C, Pressure: ${pressure} kPa`);
19
20    // Determine status based on values
21    if (temperature > 80 || pressure > 150) {
22      setStatus('critical');
23    } else if (temperature > 60 || pressure > 120) {
24      setStatus('warning');
25    } else {
26      setStatus('stable');
27    }
28  }, [temperature, pressure]); // Called when temperature or pressure change
29
30  const getStatusColor = () => {
31    switch(status) {
32      case 'stable': return '#4caf50';
33      case 'warning': return '#ff9800';
34      case 'critical': return '#f44336';
35    }
36  };
37
38  return (
39    <div style={{
40      background: 'rgba(255, 255, 255, 0.1)',
41      padding: '20px',
42      borderRadius: '12px',
43      border: `2px solid ${getStatusColor()}`
44    }}>
45      <h2 style={{ color: getStatusColor() }}>
46        Quantum Reactor - {status.toUpperCase()}
47      </h2>
48
49      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginTop: '20px' }}>
50        <div>
51          <h4>Temperature: {temperature}°C</h4>
52          <input
53            type="range"
54            min="0"
55            max="100"
56            value={temperature}
57            onChange={(e) => setTemperature(Number(e.target.value))}
58            style={{ width: '100%' }}
59          />
60        </div>
61
62        <div>
63          <h4>Pressure: {pressure} kPa</h4>
64          <input
65            type="range"
66            min="50"
67            max="200"
68            value={pressure}
69            onChange={(e) => setPressure(Number(e.target.value))}
70            style={{ width: '100%' }}
71          />
72        </div>
73      </div>
74    </div>
75  );
76}
77
78// ==========================================
79// 2. useEffect - Side Effects
80// ==========================================
81
82function QuantumClock() {
83  const [time, setTime] = useState<Date>(new Date());
84  const [quantumTime, setQuantumTime] = useState<number>(0);
85
86  // useEffect for clock - runs after component mounts
87  useEffect(() => {
88    console.log("Quantum Clock initiated");
89
90    const interval = setInterval(() => {
91      setTime(new Date());
92      setQuantumTime(prev => prev + 1);
93    }, 1000);
94
95    // Cleanup function - called on unmount
96    return () => {
97      console.log("Quantum Clock stopped");
98      clearInterval(interval);
99    };
100  }, []); // Empty array = run once after mounting
101
102  return (
103    <div style={{
104      background: 'rgba(124, 77, 255, 0.2)',
105      padding: '20px',
106      borderRadius: '12px',
107      textAlign: 'center'
108    }}>
109      <h3 style={{ color: '#7c4dff' }}>Quantum Time Portal</h3>
110      <div style={{ fontSize: '2rem', margin: '20px 0', color: '#64ffda' }}>
111        {time.toLocaleTimeString()}
112      </div>
113      <div style={{ color: '#b0bec5' }}>
114        Quantum Seconds: {quantumTime}
115      </div>
116    </div>
117  );
118}
119
120// ==========================================
121// 3. Multiple useEffect Hooks
122// ==========================================
123
124function DataMonitor() {
125  const [data, setData] = useState<number[]>([]);
126  const [average, setAverage] = useState<number>(0);
127  const [trend, setTrend] = useState<'up' | 'down' | 'stable'>('stable');
128
129  // Effect 1: Generate new data every second
130  useEffect(() => {
131    const interval = setInterval(() => {
132      const newValue = Math.floor(Math.random() * 100);
133      setData(prev => [...prev.slice(-9), newValue]); // Keep the last 10 values
134    }, 1000);
135
136    return () => clearInterval(interval);
137  }, []);
138
139  // Effect 2: Calculate average when data changes
140  useEffect(() => {
141    if (data.length > 0) {
142      const avg = data.reduce((a, b) => a + b, 0) / data.length;
143      setAverage(avg);
144      console.log(`New average: ${avg.toFixed(2)}`);
145    }
146  }, [data]);
147
148  // Effect 3: Determine trend
149  useEffect(() => {
150    if (data.length >= 2) {
151      const lastTwo = data.slice(-2);
152      if (lastTwo[1] > lastTwo[0]) setTrend('up');
153      else if (lastTwo[1] < lastTwo[0]) setTrend('down');
154      else setTrend('stable');
155    }
156  }, [data]);
157
158  const getTrendIcon = () => {
159    switch(trend) {
160      case 'up': return '▲';
161      case 'down': return '▼';
162      case 'stable': return '→';
163    }
164  };
165
166  return (
167    <div style={{
168      background: 'rgba(255, 255, 255, 0.1)',
169      padding: '20px',
170      borderRadius: '12px'
171    }}>
172      <h3 style={{ color: '#64ffda' }}>Data Stream Monitor</h3>
173
174      <div style={{ display: 'flex', gap: '5px', marginTop: '15px', height: '100px', alignItems: 'flex-end' }}>
175        {data.map((value, index) => (
176          <div
177            key={index}
178            style={{
179              flex: 1,
180              height: `${value}%`,
181              background: 'linear-gradient(to top, #7c4dff, #64ffda)',
182              borderRadius: '4px 4px 0 0'
183            }}
184          />
185        ))}
186      </div>
187
188      <div style={{ marginTop: '20px', display: 'flex', justifyContent: 'space-around' }}>
189        <div>
190          <div style={{ color: '#b0bec5', fontSize: '0.9rem' }}>Average</div>
191          <div style={{ color: '#64ffda', fontSize: '1.5rem', fontWeight: 'bold' }}>
192            {average.toFixed(1)}
193          </div>
194        </div>
195        <div>
196          <div style={{ color: '#b0bec5', fontSize: '0.9rem' }}>Trend</div>
197          <div style={{ fontSize: '1.5rem' }}>
198            {getTrendIcon()}
199          </div>
200        </div>
201      </div>
202    </div>
203  );
204}
205
206// ==========================================
207// Main App
208// ==========================================
209function App() {
210  useEffect(() => {
211    console.log("Quantum Systems Dashboard initialized");
212  }, []);
213
214  return (
215    <div style={{
216      padding: '20px',
217      background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%)',
218      minHeight: '100vh',
219      color: 'white'
220    }}>
221      <h1 style={{ textAlign: 'center', color: '#64ffda' }}>
222        React Hooks - useState & useEffect
223      </h1>
224
225      <div style={{ display: 'grid', gap: '20px', marginTop: '30px' }}>
226        <QuantumReactor />
227        <QuantumClock />
228        <DataMonitor />
229      </div>
230
231      <div style={{
232        marginTop: '30px',
233        background: 'rgba(100, 255, 218, 0.1)',
234        padding: '20px',
235        borderRadius: '10px',
236        borderLeft: '4px solid #64ffda'
237      }}>
238        <h3>useEffect Tips:</h3>
239        <ul style={{ lineHeight: '1.8' }}>
240          <li>[] (empty array) = run once after mounting</li>
241          <li>[dep1, dep2] = run when dependencies change</li>
242          <li>No array = run after every render</li>
243          <li>Return function = cleanup before unmounting</li>
244          <li>You can have multiple useEffects in a component</li>
245        </ul>
246      </div>
247    </div>
248  );
249}
250
251export default App;

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. Props in React are:

  2. 2. State in React is:

  3. 3. The useState hook is used for:

Hands-on tasks in the game

  • Code editor

    Create an async Server Component ImplantDashboard (export default). Above the component, define an async function getImplants() that returns an array of 3 implants shaped { id, name, status }. In the component, fetch the data with const implants = await getImplants(), then display them in a <ul>: each implant as an <li key={implant.id}> with its name and status.

  • Code editor

    Create a Client Component NeuralVisualizer (export default): add 'use client' at the top of the file, keep the activity in useState (a boolean, false at first), add an "Activate Neural Link" button that changes the state, and display "Status: ACTIVE" or "Status: INACTIVE".

  • Click in order

    Arrange the syntax for exporting a Server Component in Next.js

  • Code editor

    Create a Greeting component that accepts a 'name' prop and displays a personalized welcome message

  • Vertical ordering

    Arrange the steps of SSR page rendering in Next.js

  • Code editor

    Create a Counter component that displays a counter (useState, initial value 0) and a button that increases the counter by 1 on each click.

  • Click in order

    Arrange the syntax for destructuring the useState hook

  • Code editor

    Create a HolographicInterface component: export a default function that returns a div with class 'dashboard-panel', an h1 heading 'Next.js Dashboard', and a paragraph with system status.

  • Code editor

    Create a login page component LoginPage (export default). Add 'use client' at the top of the file. Return a <form> with an email field (<input type="email">), a password field (<input type="password">) and a "Log in" button (<button type="submit">). In the onSubmit handler, call event.preventDefault() so the page does not reload.

Useful articles