# React Server components (in a Next.js application) In the beginning React is a client side technology, you create components which are rendered by the browser. You don't deliver HTML+CSS directly, you deliver JS+HTML+CSS that the browser will render dynamically. As applications grow the JS part becomes bulky and your client struggles with page loading & updating. If you don't have a lot of interaction, or are willing to build this interaction in a progressive way, you can now render your React Components into the server. This helps your application performance and Next.js has evolved to use it (instead of prior [SSR/SSG data fetching](https://nextjs.org/docs/pages/building-your-application/data-fetching) approaches). ## Background Since React started there have been different ways to deal with performance issues. As your application becomes bigger, you can use techniques like Code Splitting, Lazy Loading, Browser Caching, and many more. A good design can help minimize these issues, and there are new techniques which help to even move part of the work into the server. One of these techniques is Server-Side Rendering (SSR), it has been one of the secrets behind Next.js success. But in [Next.js version 13.4](https://nextjs.org/blog/next-13-4) they have decided to start using a new APP Router and with it they now push the **React Server Components (RSC)** approach. The APP Router change is a big move and it includes dropping SSR & SSG prior approach for Next.js applications, instead you’ll need to embrace React Server Components. So even though in theory you could use these technologies together, Next.js team has decided against this. As Next.js is an opinionated framework, it is best to follow their team suggestions. You do have a leeway, but you’ll need to start thinking about this when building your new Next.js applications. ## Who is pushing for React Server Components? React Server Components (RSC) was introduced by the React team at Facebook in a [blog post](https://legacy.reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html) published on December 2020. Since then, it has grown and it was implemented in React 18 & Next.js 13. The introduction of RSC is a significant step to enhance server-side rendering capabilities within React applications. The key motivation behind RSC was to address certain challenges associated with server-side rendering (SSR) and client-side rendering (CSR) in large-scale React applications. All these technologies can co-exist, they solve similar problems in a different way. SSR and React Server Components will return HTML which is SEO friendly, in CSR server will return some HTML, an small JS and use "JSON like" data to update it (see [this explanation](https://www.youtube.com/watch?v=TQQPAU21ZUw&t=1501s) from Lauren Tan @ Meta). ## The How and Why So you are being pushed into a new way of doing things with Next.js, but why? In short the new way is a bit more JS friendly and it is using community concepts and approaches. So even though changing the way you've been doing things is a bit annoying, in my opinion it is much easier to understand for people starting fresh. The new RSC provides better results, hides complexity and lets you use a similar approach for things running on the server and on the client. It is only similar, there are some limitations on what you have available in the server (if you are used to React already). We will cover this in the next section, and there is a very informative video from Lee Robinson ([Incrementally adopt the Next.js App Router](https://www.youtube.com/watch?v=YQMSietiFm0)) which shows how to migrate from PAGE to APP router. So to sum up, why? - As with SSG you have a faster initial load. - You can send components separately which makes it easier to keep some parts static and others dynamic. - Server Components allow you to move data fetching to the server, closer to your data source (which gives you speed & security benefits). - You can keep large JS dependencies in the server. This helps you to reduce the client side load, the code that goes to the client is smaller. - Keep being SEO friendly (you can even now add [Metadata](https://nextjs.org/docs/app/building-your-application/optimizing/metadata) to your pages). ## Show me the code! We will see React Server Components within a Next.js application, if you are new to Next.js we have a series that could bootstrap you: - [First steps with Next.js](https://weknowinc.com/blog/first-steps-nextjs) - [My first app using NextJs and MongoDB](https://weknowinc.com/blog/my-first-app-using-nextjs-and-mongodb) - [Creating a new page and layout using Next.js](https://weknowinc.com/blog/creating-new-page-and-layout-using-nextjs) - [Adding context to our application in Next.js](https://weknowinc.com/blog/adding-context-our-application-nextjs) The difference in the above series is the use of the old page-router, in this example we use the new app-router and Typescript instead. In these examples the Next.js application will only be a shell, so hopefully you can simply accept it as part of the context. I have created a super simple project using [create-next-app](https://nextjs.org/docs/getting-started/installation): ```bash npx create-next-app next-react-components ``` Using the suggested configuration: - Use TypeScript: **Yes** - Use ESLint: **Yes** - Use Tailwind CSS: **Yes** - Use `src/` directory: **Yes** - Use App Router: **Yes** - Would you like to customize the default import alias: **No** The full code is available at [Next RSC example github repo](https://github.com/ibonelli/Next-React-Server-Components). After creation I tweaked the landing page (src/app/page.tsx), removed lots of the created coded and added instead the following two components: ```jsx <ServerComponent /> <ClientComponent /> ``` As the default way of working in Next.js 13.4 onwards is using React server components, when we create a React component it will be a server one. So if I create a file `src/app/ServerComponent.tsx` and enter: ```jsx import React from 'react'; import { getData } from '@/lib/getData'; const ServerComponent = () => { // Accessing server data directly as this will run on the server const MyList: any = getData(); return ( <> <h1>React Server Component</h1> <ul> {MyList.map((item: any) => ( <li key={item.name}>{item.name} - {item.age}</li> ))} </ul> </> ); }; export default ServerComponent; ``` I'll get a server component, if I want to turn the component into a client component I need to add to it in the beginning the macro: `"use client"`. So we will also create the file `src/app/ClientComponent.tsx` and add the following code: ```jsx "use client" import React, { useEffect, useState } from 'react'; const ClientComponent = () => { const [myList, setMyList] = useState<any>([]); useEffect(() => { const fetchData = async () => { try { // REST call to a service running on the server const response = await fetch(`/api/namelist`); if (!response.ok) throw Error('Did not receive data'); const data = await response.json(); setMyList(data.MyList); } catch (error) { console.error("Could not fetch past touchbase data.") } }; fetchData(); }, []); return ( <> <h1>React Client Component</h1> <ul> {myList.map((item: any) => ( <li key={item.name}>{item.name} - {item.age}</li> ))} </ul> </> ); }; export default ClientComponent; ``` As you can see the component HTML and data is the same for both, what changes is how we fetch the data and where it is being rendered. You will also need some backend code to be able fetch the data. I created a function/method that gets the data in the server (src/lib/getData.ts): ```typescript import fs from 'fs'; import path from 'path'; export function getData() { const os = require('os'); const filePath = path.resolve("./", "namelist.json"); var MyList: any; try { const fileContent = fs.readFileSync(filePath, 'utf-8'); MyList = JSON.parse(fileContent); } catch { MyList = [{ "name": "Error", "age": "Missing namelist.json file" }]; console.error("The file name_list.json is missing!"); } return MyList; } ``` The data is stored in the file ´namelist.json´ which is on the root of the repository. And to be able to access this data as a REST service you'll need to also create an API route. This is simple in Next.js, just create a file called `src/app/api/namelist/route.ts` and add the following code: ```typescript import { NextResponse } from 'next/server' import { getData } from '@/lib/getData'; export async function GET() { const MyList: any = getData(); return NextResponse.json({ MyList }, { status: 200 }) } ``` For now, that's it. You'll get a simple application showing a list of names within a the server component and the client component (both with the same data): ![Capture image of the application so far](https://hackmd.io/_uploads/rk3bhqBYa.png) One important thing you'll notice when running the example is that the RSC (above) will render with the page, and that the RCC (below) will have a delay before it can show the data. This happens because the RCC needs to fetch the data after being rendered. This is an important thing to keep in mind, RSC will load faster. Everything is prebuilt and sent to the client, so you have it available at page load. The catch with RSC is adding interaction to them, is not as we are used to (hooks won't work). In summary when using React Server Components (RSC) you can access server data directly, when using React Client Components (RCC) you'll be running on the browser and need to rely on REST services. That's it in a nutshell, but [this page of the Next.js documentations](https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns) has a comprehensive table. ## Use of hooks and passing props Another important change between RSC & RCC is the usage of hooks and how to integrate one with the other (there is a "Network Boundary", see image below). You can make them work together, but they do not coexist at run time. You need to keep this in mind when composing them together. ![Network boundary separating RSC from RCC](https://nextjs.org/_next/image?url=%2Flearn%2Flight%2Flearn-client-and-server-environments.png&w=1920&q=75&dpl=dpl_8o7rHv6dvLgmVRtPqGMZEUrA9b9T) A server component cannot use React hooks like useState, useReducer, useEffect, etc... This is because a server component is rendered on the server and does not have access to hooks that can affect the DOM (Document Object Model). Passing anything other than props is also a problem. A method/function that lives on the server can not be passed to a RCC. So you can still do whatever you did within your client side, but you can not share it with RSC. Any changes on the server component will require a page reload. But as I said you can pass props, let me show by example. We will create two additional component, first the server one `src/app/Parent_RSC.tsx`: ```jsx import React from 'react'; import Child_RCC from './Child_RCC' import { getData } from '@/lib/getData'; const Parent_RSC = () => { const MyList: any = getData(); return ( <> <h1>Parent RSC</h1> <p>Fetching data and passing props.</p> <Child_RCC list={MyList} /> </> ); }; export default Parent_RSC; ``` Which is similar to the RSC we created initially, but in this case we pass on the data as a prop to the RCC `Child_RCC.tsx`: ```jsx "use client" import React, {useState} from 'react'; const Child_RCC = (data: any) => { const myList: any = data.list; const [state, setState] = useState<boolean>(false); const handleButtonClick = () => { if(state == true) { setState(false); } else { setState(true); } }; return ( <> <h1>Child RCC</h1> <ul className='list' hidden={state}> {myList.map((item: any) => ( <li key={item.name}>{item.name} - {item.age}</li> ))} </ul> <br/> <button className='button' onClick={handleButtonClick}> Click To Hide Items </button> </> ); }; export default Child_RCC; ``` In this example I also added some classical RCC behavior with a hook. Once we are within the RCC there is no problem to use hooks, you simply use what you are used to. The only limitation is the integration between the two. You can compose them, you can pass props, but that's it. ![Screenshot of the full application](https://hackmd.io/_uploads/B16MbnHKa.png) ## Where do we go from here? Well, I hope I gave you enough to do a fair amount of things. This should cover a good chunk of what you will normally do and explain what changes. But in case you want to know more or go to the sources, here you have some links to official documentation: - [Introducing Zero-Bundle-Size React Server Components](https://react.dev/blog/2020/12/21/data-fetching-with-react-server-components): A video and links which were the initial steps of RSC. - [Server Components Rendering Options](https://nextjs.org/docs/app/building-your-application/rendering/server-components): Everything described here is called RSC "static rendering" (which is the default behavior). In some situations you might want to use "dynamic rendering" & "streaming". Both techniques will give some flexibility to RSC. - [Server and Client Environments](https://nextjs.org/learn/react-foundations/server-and-client-components): This article explores a bit more the network boundary and the difference between RSC & RCC environments. - [router.refresh() to update your RSC](https://nextjs.org/docs/app/api-reference/functions/use-router): In most cases updates to a RSC will come with a reload of the route. Getting into the router goes beyond this article, but it is something to explore as well. - [Server Actions and Mutations](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations): This was released in Next.js 14, but the time of writing this it uses [useFormState](https://react.dev/reference/react-dom/hooks/useFormState) & [useFormStatus](https://react.dev/reference/react-dom/hooks/useFormStatus) hooks which are only available in React early [Canary releases](https://react.dev/community/versioning-policy#canary-channel). It allows you to use these server actions functions instead of writing a separate API call.