Tutorials on Server Side Rendering

Learn about Server Side Rendering from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

How to Consume a GraphQL API in Next.js with urql and next-urql

In this article, we will learn how to use urql to consume a GraphQL API in a Next.js SSR app.urql is a lightweight, versatile and extensible GraphQL client for modern frontend apps, with support for React, Svelte, Vue and plain JavaScript. It was introduced as an alternative to existing GraphQL clients like Relay and Apollo . These GraphQL clients are largely similar when it comes to setup and use. In the next section, we will see how they compare with each other on various parameters. All three GraphQL clients provide standard features such as queries, mutations, caching and subscriptions, while only differing slightly in implementation. urql 's defaults for configuration are slightly better than those for Apollo, and it has a lower entry barrier thanks to its thorough documentation and native support for features otherwise only available via third-party plugins. Additionally, urql has a powerful caching mechanism offers both normalized and document caching via the @urql/exchange-graphcache package. urql is built on the principle of having a lightweight core, extensible via middleware called exchanges. This makes it the smallest in bundle size as compared to the other options. A full, detailed comparison of the clients by different categories of features can be found on the urql website . Next.js is one of the most popular React-based frameworks, and urql has first class native support for it via the next-urql package. Apollo and Relay do not have official plugins with support for Next.js which means the implementation might change between releases of the framework, and any app that uses it will have to be constantly maintained to keep up. With next-urql , most of the boilerplate involved in setting up urql for Server-Side Rendering (SSR) with Next.js is already done for you. It provides convenience functions such as the withUrqlClient HOC which enables your SSR pages to pre-fetch data via GraphQL queries. Next.js requires Node to be pre-installed on your system. You can then scaffold a Next.js TypeScript app using the following command in your terminal/command prompt. Once you have a skeleton app set up, you can install the dependencies required for urql . graphql is a peer dependency of urql and provides the underlying GraphQL implementation. No additional type definitions are required since urql is written in TypeScript. next-urql provides the Next.js bindings for urql . react-is is a peer dependency of next-urql , required for react-ssr-prepass to walk the component tree and pre-fetch any data required for rendering. We can use the withUrqlClient HOC to wrap our entire app in the urql context. This makes the urql client and hooks usable in the rest of our app. The first parameter to withUrqlClient is a function that returns a ClientOptions object. This can be used to pass configuration into the urql client instance, such as the API URL, custom fetch function, request policy and any additional middleware in the exchanges property. For this tutorial, we will use the GitHub GraphQL API. This API requires you to authenticate using a personal access token. You can follow the steps described here to create one after logging in with your GitHub account. We can then configure our urql client to pass the token as part of the authorization header on each request to the API. Now that we have our urql client set up, let us look at how we can use it to connect to the GitHub API and fetch some data. We will build a simple component that will display a list of repositories, with each item showing a link to the repo, its name, star count and commit count. The component will look somewhat like this. To start, let us look at the GraphQL query that should be used. This query fetches the first 10 repositories for the current user (determined from the personal access token). For each repository, it includes the name, ID, URL, stargazer count and the number of commits to the main branch. There are various other fields that can be added to the query, as documented in the API reference . We can execute this query using the useQuery hook from urql . Since we're using TypeScript, let us model the API response with the correct expected types and use them as type parameters to useQuery . The response object returned by useQuery returns a number of useful items, out of which we will currently use the fetching flag which tells us whether or not the operation is still in progress, and the data property which contains the fetched data when available. Let us now add some simple UI to render the returned data. This Repositories component now fetches and renders a list of repositories with star and commit counts. So far, we've seen how to set up urql in a Next.js app and use it to query the GitHub GraphQL API. Let's now take it a step further and learn how to create mutations - these are API operations that can cause the data to change in the backend. For the purposes of this tutorial, we will implement the creation of an issue within a given GitHub repository. The GraphQL mutation to create an issue looks like this: This mutation takes three variables - the repository ID to create the issue in, the title and the body of the issue. On success, it returns an Issue object that can contain the number, title and body. So let us model the request variables and response, and create the mutation. The useMutation hook returns a tuple with two items - an object that exposes the current state of the mutation request, and a function that can be invoked with input variables to execute the actual mutation. Let us adapt our Repositories component to be able to call this mutation. We'll refactor and extract some of the code into an individual Repository component along the way. This is what the refactored Repositories component will look like. All the GraphQL types have been moved to a separate types module. And the individual Repository component now renders the list item, along with a button that invokes the createIssue mutation when clicked. Clicking the button creates an issue with a sample fixed title and body in the corresponding repo. Every query or mutation in urql is modeled as an 'operation', and the system at any moment has a stream of operations issued by various parts of the app. Exchanges are pieces of middleware that transform the stream of operations into a stream of results. This is explained in more detail in the architecture documentation . Some of urql 's core features such as fetching data and caching are also handled via exchanges implemented by the urql team and provided by default. You can also create your own exchanges by implementing functions that conform to the rules defined here . Server-Side Rendered apps need to be set up to fetch data on the server-side and send it down to the client for hydration.  urql  supports this via the ssrExchange . The SSR exchange has two functions - it gathers all the data as it is being fetched server-side, and using the serialized data on the client side to rehydrate the app without a refetch. When using next-urql , most of the boilerplate involved in instantiating the ssrExchange is already done for you. So if your client does not use any other exchanges, you do not explicitly need to instantiate the ssrExchange when creating the client. To enable SSR, you simply need to set the ssr flag in the second argument to the client configuration function. If you do want to add other exchanges to your client, they can be specified in an exchanges property returned by the configuration function. This function also gets the instance of the ssrExchange passed into it when called. Enabling SSR when wrapping the top level App component in withUrqlClient disables Next's ' Automatic Static Optimization ' which allows for hybrid apps with both server-side rendered and statically generated pages. If this is required in your app, you can wrap individual page components with withUrqlClient as required. When applying withUrqlClient to specific pages, we can also use getStaticProps or getServerSideProps to pre-fetch the data and populate the urql cache. This will render the page as a static page, further optimizing performance and allowing us to perform other operations in these functions. Let us adapt our app to use server-side rendering with getServerSideProps for our repositories component. We will add getServerSideProps to our Home page component, as this function can only be exported from page components. The getServerSideProps function gets called when the page is being rendered server-side. It will populate the cache so that the subsequent render of the Repositories component will hydrate it from cache when useQuery is called. In this article, we have learnt how to set up urql with a Next.js app and perform queries and mutations against the GitHub GraphQL API. Further, we have also learnt about the architecture of urql and how Exchanges work. We have used next-urql to implement server side rendering with pre-fetching of queries using the urql client and cache. In a subsequent tutorial, we will learn how to use urql exchanges for authentication and caching. All the code used in this article is available on my GitHub .

Thumbnail Image of Tutorial How to Consume a GraphQL API in Next.js with urql and next-urql

Web Components in Server-Side Rendered (SSR) and Static-Site Generated (SSG) in Next.js Applications

Compared to other web development frameworks, Next.js has become very popular because of its support for a number of rendering strategies that result in highly performant frontend applications: By having the content readily available in the initial HTML document, less client-side rendering takes place. All the client has to do is hydrate the components and make them functional. The less dynamic content the client renders, the better the application's performance and SEO. Search engine bots like Googlebot can visit the application, immediately crawl (and understand) its content and rank it high on search engines. If you use Web Components to build your application's UI, then you don't need to worry about the breaking changes that third-party JavaScript frameworks/libraries notoriously cause (as a result of rapid development and release cycles). Your application performs equally as well as (or better than) applications that use these frameworks/libraries. Additionally, the low-level, native APIs of Web Components are based on an official W3 specification and let you develop encapsulated, modular and reusable components (represented as custom HTML elements). However, you will come across several limitations that can prevent the application from taking advantage of these rendering strategies: Custom web components are defined with a class that extends from HTMLElement and registered using the customElements.define method. Since browser APIs do not exist in the context of Node.js, Next.js's build tools cannot run code that uses these APIs. This also applies to the Shadow DOM . The Shadow DOM's imperative nature prevents custom web components from being rendered by either the server or build tools. The missing browser APIs can be "polyfilled" and "shimmed" for these environments before the custom web component is defined with the class keyword. This approach makes it possible to simulate the DOM and output an HTML string for the initial HTML document, but it lacks the ability to hydrate components. Fortunately, the Declarative Shadow DOM provides an alternative way for implementing web components that complements server-side rendering and static-site generation. Below, I'm going to show you how to pre-render custom web components in Next.js. By extracting the logic used to create the template that is attached to the component's innerHTML into a separate module, the server (or build tools) never has to run code that uses browser APIs like HTMLElement and customElements . For any custom web component that has a shadow root attached to it, we will use the Declarative Shadow DOM so that the shadow root is attached to it and readily available at the time of the component's instantiation. To get started, scaffold a new Next.js application with the TypeScript boilerplate template. Once the application has been scaffolded, change the current directory to the application's directory, like so: In this tutorial, we want to test that the custom web components are pre-rendered when the application is server-side rendered or static-site generated. Next.js provides several CLI commands for building and running the application: dev , build , start and lint . For server-side rendering, we want to build a production version of the application and spin up a Next.js production server that runs getServerSideProps on each incoming request and pre-renders this application with the returned props. Since next start starts the server, let's add a prestart npm script that runs the build npm script, which builds the application via the next build command. ( package.json ) For static-site generation, we want to export the application to static HTML. Let's add an export npm script that exports the application to static HTML via the next export command. Since the application must be built prior to being exported, let's also add a preexport npm script that runs the build npm script. ( package.json ) Within the pages/index.tsx file, remove the children elements under the <main /> element, and replace the text within the <footer /> element's child <a /> element with the text "Powered by Vercel." ( pages/index.tsx ) Now that there are no more <Image /> components in the application, let's remove the import Image from 'next/image' line from the pages/index.tsx file. This Next.js application will feature two custom web components: Since the <pinned-location-link /> component will not use a Shadow DOM, let's create this component such that its rendering logic (what's set to innerHTML ) is completely decoupled from its class declaration. First, create a directory named components , which will house these custom web components. Within this directory, create two subdirectories, pinned-location-link and info-tooltip , each of which will contain two files: Let's define and register the <pinned-location-link /> component. To keep things simple, components' props will only come from their attributes. ( components/pinned-location-link/component.ts ) Note #1 : When using these components in JSX/TSX code, attributes must be prefixed with data- (custom data attributes). Otherwise, TypeScript will raise the following error: Property '<attribute>' does not exist on type 'DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>' . Note #2 : connectedCallback is a special lifecycle callback that gets called after the custom element is inserted into the DOM. Note #3 : To fix the TypeScript error Type 'NamedNodeMap' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators. on [...this.attributes] , set the downlevelIteration option to true under the compilerOptions in the tsconfig.json file. This allows you to iterate over iterators in TypeScript code. Attributes for any node are represented as a NamedNodeMap , not as an Array . The connectedCallback lifecycle callback... Since all JSX elements come with a dangerouslySetInnerHTML property, this approach allows Next.js to not have to run the component.ts file to set the component's innerHTML . You don't have to polyfill and shim browser APIs like HTMLElement and customElements for Next.js's build tools. Let's write the template function that generates the component's stringified HTML from the passed props. ( components/pinned-location-link/template.ts ) This template function supports several types of location search queries used in a Google Maps URL, each of which is optional: Based on the passed props, we can add a class to the <a /> element in the stringified HTML. We can also modify the Google Maps URL and the text displayed within its child <span /> element. Inside of the <main /> element of the <Home /> page component in the pages/index.tsx file, add the <pinned-location-link /> component, like so: ( pages/index.tsx ) Currently, Next.js doesn't know what the HTML content inside of the <pinned-location-link /> component is supposed to be. However, if we give it the dangerouslySetInnerHTML property and set the __html key to the result of the component's template function, then Next.js can render the component's HTML content without ever having to define or register it as a custom element. To demonstrate this, let's import the template function from the components/pinned-location-link/template.ts file and add the dangerouslySetInnerHTML property to the component, like so: ( pages/index.tsx ) Note : If any data comes from a third-party service, then you may want to pass the result of template to an HTML sanitizer like DOMPurify to mitigate cross-site scripting (XSS) attacks. Unfortunately, TypeScript raises the following error: Property 'pinned-location-link' does not exist on type 'JSX.IntrinsicElements' . Since the <pinned-location-link /> component is not a React component or standard HTML element, TypeScript does not recognize it as a valid JSX element. Therefore, we must create a type definition file ( types/index.d.ts ) that lets us add custom elements to the JSX.IntrinsicElements global interface: ( types/index.d.ts ) With the TypeScript error no longer popping up in VSCode, let's build the application and run it in production mode: Note : Add export {}; to the empty components/info-tooltip/component.ts and components/info-tooltip/template.ts files. Otherwise, the build will fail. Within a browser, visit localhost:3000 . Then, disable JavaScript and reload the page. When you inspect the elements on the page, you will see that the HTML content of the <pinned-location-link /> component still exists despite JavaScript being disabled. Now, let's define and register the <info-tooltip /> component. Unlike the <pinned-location-link /> component, this component will use the Shadow DOM. A hidden, separate DOM will be attached to the component (the custom element becomes a shadow host). All CSS rules defined within the Shadow DOM will be scoped to elements that are part of the shadow tree, and CSS rules outside of the Shadow DOM that specify the same selectors will have no effect on these elements. The Shadow DOM will be implemented declaratively, not imperatively. The Declarative Shadow DOM involves wrapping the component's content in a <template /> element with a shadowroot attribute. When this attribute is set to open , the shadow root's internal features can be accessed with JavaScript. On the other hand, imperatively setting up the shadow root involves calling the Element.attachShadow method to attach a shadow tree to the custom element. ( components/info-tooltip/component.ts ) Since both the <pinned-location-link /> and <info-tooltip /> components extract props from data attributes, we should refactor this logic into a function named extractPropsFromAttrs so that it can be shared by both components. ( shared/index.ts ) Don't forget to update the components/pinned-location-link/component.ts file so that the connectedCallback lifecycle callback calls this method. ( components/pinned-location-link/component.ts ) Once you have made that update, create the <info-tooltip /> component's template. The <info-tooltip /> component only accepts two props: ( components/info-tooltip/template.ts ) Unlike the <pinned-location-link /> component's template, the <info-tooltip /> component's template gets wrapped within a <template /> element by default during server-side rendering or static-site generation. If the Declarative Shadow Root does not exist when the page is initially loaded, then via client-side rendering, the shadow root gets set up imperatively with the Element.attachShadow method. The <template /> element is only for the Declarative Shadow DOM. For us to use it in the JSX code, add the <info-tooltip /> custom element to the JSX.IntrinsicElements global interface. ( types/index.d.ts ) With everything completed, let's render this component within the <Home /> page component. Import the template function from the components/info-tooltip/template.ts file and add the dangerouslySetInnerHTML property to the component, like so: Note : Since both files, components/info-tooltip/template.ts and components/pinned-location-link/template.ts , export a template function as the default export, both functions will need to be renamed to avoid conflicting function names. Let's rebuild the application and run it in production mode: Within a browser, revisit localhost:3000 . JavaScript should still be disabled from before. When you inspect the elements on the page, you will see that the HTML content of the <info-tooltip /> component lives within a Shadow DOM despite JavaScript being disabled. Suppose we re-enable JavaScript. If we add data attributes to the <pinned-location-link /> and <info-tooltip /> components rendered in the <Home /> page component, then the components should initially be rendered with the props passed to the template function for the dangerouslySetInnerHTML property. Once client-side rendering takes place, the components should be rendered with the props extracted from the data attributes. Let's add some data attributes to these components. ( pages/index.tsx ) Then, rebuild the application and run it in production mode: When you reload the page, you will see that nothing has changed. This is because we have not yet imported the component.ts files into the Next.js application. Since the server and Next.js build tools do not run the useEffect lifecycle hook, let's dynamically import these files in useEffect . After the initial render of the page, useEffect will run on the client-side, import these files and execute them. These custom elements will officially be registered by the browser, and the component will be updated and re-rendered using the data attributes. ( pages/index.tsx ) Then, let's temporarily modify the components/info-tooltip/component.ts file so that regardless of whether or not the Declarative Shadow Root exists as a result of SSR/SSG, the <info-tooltip /> component's content will be updated using the props extracted from the component's data attributes: ( components/info-tooltip/component.ts ) Rebuild the application once more and run it in production mode: When you reload the page, you will see that the content of the components is initially rendered via the dangerouslySetInnnerHTML property. As soon as the client fetches the component.ts files and executes them, the content of the components updates accordingly with the data attributes set on these components. If you disable JavaScript and reload the page again, then the client never fetches the component.ts files, and you fallback to the components in their initial render state once more. Afterwards, undo the changes made to the components/info-tooltip/component.ts file. Now let's test that the application works properly when static-site generated. Run the following command to export the application as static HTML: With the static HTML (e.g., index.html and 404.html files) exported to an out directory by default, serve the directory as a static site via serve : Within a browser, visit localhost:3000 and reload the page with JavaScript enabled/disabled to verify everything still works properly. Currently, only Google Chrome and Microsoft Edge natively support Declarative Shadow DOM. To provide support for this feature in other browsers, we must use the @webcomponents/template-shadowroot ponyfill . Unlike polyfills, which are found in HTML document's <head /> , ponyfills are found at the end of the HTML document's <body /> . Since it gets executed after the DOM is fully loaded, the ponyfill will be able to find all the <template /> elements that have a shadowroot attribute and convert them into shadow roots on the <template /> elements' parent elements (done via the ponyfill's hydrateShadowRoots method). In the Next.js application, we will dynamically import the ponyfill in the useEffect lifecycle hook after the <Home /> page component's initial render. Once the ponyfill has been successfully imported, we check if the client natively supports Declarative Shadow DOM. If not, then we call hydrateShadowRoots on the HTML document's <body /> . Afterwards, we proceed to dynamically import the two component.ts files like before. First, let's install the @webcomponents/template-shadowroot ponyfill: Then, dynamically import the ponyfill in the useEffect lifecycle hook, like so: ( pages/index.tsx ) If you find yourself stuck at any point while working through this tutorial, then feel free to visit the main branch of this GitHub repository here for the code. Try passing data to custom web components from props returned by getServerSideProps and getStaticProps . If you want to learn more advanced techniques with web components, then check out the Fullstack Web Components  book by Steve Belovarich, a software engineer who has many years of industry experience building out enterprise-grade web applications.

Thumbnail Image of Tutorial Web Components in Server-Side Rendered (SSR) and Static-Site Generated (SSG) in Next.js Applications

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Server Side Rendering with React

In this article, we will learn how to render a simple React app using an express server.Server Side Rendering (SSR) can be a useful tool to improve your application's load time and optimise it for better discoverability by search engines. React provides full support for SSR via the react-dom/server module, and we can easily set up a Node.js based server to render our React app. In the following sections, we will create a simple counter app with React and render it server-side using an express backend. Let us use create-react-app to generate a stock React app. We'll call our app ssr-example . Let us modify the App.js file to implement a simple counter component that displays the current count. It will also render buttons with which the counter can be incremented and decremented. In our app's index.js file, we have the following line which tells React where to render our app. This needs to be slightly modified to work with SSR. To be able to render our app, we must first compile it so that an index.html and the compiled JavaScript is available. You can build the app with the following command: We will use express.js to set up a simple server for our app. You can install it using the following command in your project folder: Since the server needs to be able to render JSX, we will also need to add some babel dependencies. We will also install ignore-styles since we do not want to compile CSS. Let us create a server using the express module we have just installed. To start, create a folder called server in your project folder, and create a server.js file within it like so: We have just defined an express app that will listen on port 8000 when started. With the app.use() statement, we have also set up a handler for all requests to routes matching the ^/$ regular expression. In the next step, we will add code in the handler to render our app. But before we move on to that, we will need to configure our babel dependencies to work with the server we have just defined. To do so, create an index.js file in the server folder with the following code that imports the required dependencies, and calls @babel/register : Let us now add the code that actually renders our app. For this, we will use the fs module to access the file system and fetch the index.html file for our app. If there is an error reading the file, we will return a 500 status code with an error message. Otherwise, we can proceed with the rendering. The index.html has a placeholder element, usually a div with the ID root where it renders the React app. We will use the renderToString function from react-dom/server to render our App component as a string, and append it to the placeholder div . And that is pretty much it! We're now just one step away from being able to get this up and running. Let us add an ssr script to our package.json file to run the server. You can now start the server from your terminal with the command yarn ssr . When you navigate to http://localhost:8000 in your browser, you will see the app rendered as before. The only difference will be that the server responds back with the rendered HTML this time around. We have now learnt how to implement Server Side Rendering with a React app using a simple express server. The code used in this article is available at https://github.com/satansdeer/ssr-example , and a video version of this article can be found at https://www.youtube.com/watch?v=NwyQONeqRXA .

Thumbnail Image of Tutorial Server Side Rendering with React