react server components - International JavaScript Conference Fri, 19 Jul 2024 15:21:55 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://javascript-conference.com/wp-content/uploads/2017/03/ijs-favicon-64x64.png react server components - International JavaScript Conference 32 32 Is Remix Better Than Next.js? https://javascript-conference.com/blog/is-remix-better-than-next-js/ Wed, 27 Sep 2023 08:12:08 +0000 https://javascript-conference.com/?p=89517 Remix specializes in server-side rendered websites and promises a better developer and user experience and faster load times with an innovative approach to routing. In the React ecosystem, Next.js has established itself as the technology for dynamic websites. Since the late 2021, Remix is a new contender from the makers of React Router. Let’s see an overview of Remix using a sample application and compare it to Next.js.

The post Is Remix Better Than Next.js? appeared first on International JavaScript Conference.

]]>
Installing and setting-up Remix

Remix can be installed using npm, as is common for JavaScript frameworks. You can simply create a new project using npx create-remix. In doing so, the script asks the user for preferences, such as the use of TypeScript or a preferred server implementation (Express, Vercel, Netlify, etc.), and creates a minimal project framework. Remix also offers more complex templates where pre-built stacks are preconfigured with database solution, integration and unit tests, deploy pipeline, and hosting provider. However, we want to focus less on these stacks and more on the core concepts of Remix.

Server-side rendering

Remix is meant for server-side rendered websites. But what does that actually mean? Rendering describes the process of an HTML document getting populated with content. A basic distinction is made between client-side and server-side rendering (Fig. 1).

Fig. 1: Overview of client-side and server-side rendering with hydration

For example, if you create a React single-page application with create-react-app and deploy it to a web host, an HTML document is downloaded when you visit the website. However, the content of this document is limited to references on styles and scripts needed to build the website. Constructing the HTML structures only occurs in the browser, which is why we call it a client-side rendered website (CSR). It’s important to know that usually, all CSR routes sites are defined by the same HTML file, which also means that for a crawler the same metadata is visible for all routes. This is bad in web stores, since different embeddings are not created for different product routes.

If the web server fills the HTML document with content before transmission, this is called server-side rendering (SSR). Since the documents are created dynamically, the aforementioned problem with metadata is eliminated. However, the website takes longer to respond because the server must first assemble the response and cannot simply copy it from memory as it would with a static host.

Between CSR and SSR, you can still find Static Site Generation (SSG). Here, all site routes are pre-rendered and the HTML documents of all routes are uploaded to the web host. This approach can be interesting if the site contains few static routes, such as a portfolio or documentation site. For a web shop with hundreds of products that can be edited on the fly, this approach isn’t suitable.

Solutions from Remix

Remix creates the server that serves the React application as a framework. When a route is invoked, the Remix server renders the HTML document before it is transmitted to the user. The browser can render the site immediately. JavaScript is executed in the background, effectively making the site a client-side single-page application. The step where the client does the rendering is called hydration. As a result, Remix provides the benefits of SSR, such as dynamic metadata and faster rendering of content in the browser, as well as the benefits of CSR, such as making changes to the site without reloading an HTML document.

One problem with server-side pre-rendered web pages with client-side hydration is often that the page has already been rendered for the user before hydration, but is not interactive yet (Fig. 1, right column). Remix solves this problem by building the data concept on HTML forms. More on that in the section “Data Flow and Hydration”.

iJS Newsletter

Join the JavaScript community and keep up with the latest news!

[mc4wp-simple-turnstile]

Routing in Remix

Remix uses the directory to define routes. All JavaScript module files stored under app/pages are interpreted by Remix as route components. The name of the file defines the route segments. For example, a module under app/pages/products.tsx defines the /products route. Subfolders can be created for deeper routes. Variables can be defined in the file or folder name with a $ sign. For example, a file defined under app/pages/products/$productId.tsx will be included in all products/* routes.

The dropped files export a common React component via the default export. Features provided by React – such as hooks – can also be used here. In addition to the default export, you can provide other optional exports that can be used to query data, define troubleshooting, and declare metadata. But more about that later.

An interesting feature of Remix routing is nesting. Remix assumes that websites are usually hierarchical. For example, all routes on a website have the same navigation bar, account settings all have a sub-navigation, or all product pages have a consistent layout. Therefore, the modules stored under app/pages do not define the entire routes, but only route segments, as Figure 2 shows. Here, a product website is shown assembled from three segments (root component, $product-Id.tsx, and $variantId.tsx), and how the called URL is mapped to the route components.

Fig. 2: Visualization of nested routing.

 

The nesting is enabled by an <Outlet/> component provided by Remix. This can be included in the render function of a segment. When segments are assembled, the subsequent segments are included there (Fig. 3).

Fig. 3: Segments of a route are joined together

 

When rendering a route, Remix starts at the root under app/root.tsx and includes the next route segment in the <Outlet/> component. The next segment is then included in the included component. This continues until the whole URL has been mapped.

The question may arise, how does routing work with index pages? For example, if you want to list all categories on the /categories route, the obvious thought would be to put that listing in the app/routes/categories.tsx. However, the problem is that more specific routes, like /categories/123, would also render this listing. Remix solves this problem by using index segments. A module named index.tsx can be created in the app/routes/categories folder. Remix will prefer this index segment if it is the last element of the path (Fig. 4).

Fig. 4: Mapping of index routes and regular routes in the file system and in the URL.

 

Individual segments are rendered in parallel and in isolation from each other, and are assembled once all segments are complete. Isolation means that data cannot be shared between route segments, which may cause them to be requested multiple times. However, the advantage of this strategy is if errors occur in one segment, you can guarantee that the other segments will continue to work. Remix builds the error recovery strategy on this. You can export a catchBoundary and an errorBoundary in a route segment.

Functions are React components, just like the default export. If there is an error, the content of these React functions will be displayed instead of the actual content. Since the segments are independent of one another, the overlying segments remain interactive. If catchBoundary and errorBoundary are not defined, Remix will go up the segments to the root until a catchBoundary or an errorBoundary is found.

EVERYTHING AROUND ANGULAR

Explore the iJS Angular Development Track

Data Flow in Remix

An important part of any web site is data. A web store, blog, or news site without data is … well, not exactly worth reading. Remix provides a data flow integrated with the routing. Each segment can provide a loader and an action function. These functions, unlike error and catchBoundary, are not JSX components, but return a Response. Loader and action can be compared to query and mutation, which are familiar from GraphQL.

Loaders are read operations, such as querying product information or blog entries. Actions, on the other hand, are write operations. They are used in log-ins, creating blog entries, or submitting comments, basically anywhere that modifies the state returned by the loader function.

The data flow can be represented by a triangle (Fig. 5). The corners represent the render, loader, and action functions. When a user calls the page, the loader first provides the required data. The data is passed to the render function. It can then define a form that calls an action. It modifies the state on the server, which also changes the state of the loader, leading to a change in the rendered content.

Fig. 5: Interactions between render, action, and loader functions.

 

It’s important to note that the loader and action function are not included in the client JS bundle. Each call to these functions happens on the Remix server. This also means that the function does not have access to browser APIs such as localStorage or sessionStorage. If you want to keep data between the browser and the server, a session cookie is recommended. Remix provides a cookie implementation that stores the data as a base64-encoded string. Listing 2 shows a session cookie in use.

The loader function must return a Response object. Response is a part of the Node.js API and describes an HTTP response. Response allows to return any content and set all headers. Through these headers, Remix also allows redirects or setting session cookies.

Remix provides some helper functions that abstract Response, such as the json function used in Listing 1 [1]. The data returned by the loader function can be used in the render function via the useLoaderData hook (Listing 1)

However, you usually want to pass data as well. For example, logging in without a way to pass the username and password is not really useful. The action function is used for this purpose. Actions are defined according to the same pattern as loader (Listing 2).

Accordingly, the actions can be called using HTML forms. Remix provides its own Form component, which is pre-rendered as a regular HTML form and replaced after hydration. So, the page remains usable even if it is not hydrated yet or JavaScript is disabled in the browser.

The values to be transferred are defined in <input> elements. If you want to define multiple actions per segment, Remix has the convention of “Intent”. Here, a hidden input field is used to define an action name, which is also transmitted when the form is submitted. On the server, the input field can be read and the correct logic executed. A log-in segment might look like the one shown in Listing 3. Two intents login and logout are defined in it. The relevant branch is then executed in the action. The beauty of this model is the separation between logic and rendering and the possibility of automatic revalidation.
 

After an action is invoked, Remix automatically triggers the loaders of all active segments in the hydrated state. If there are any changes to the site as a result of the action, Remix automatically makes them visible immediately.

Data Flow and Hydration in Remix

Behind the scenes, Remix creates an endpoint for each action and loader function through which the JSON data is accessed when the application is hydrated. This way, for new data, only the relevant data is transferred in JSON format, rather than the entire HTML document. If the application is not yet hydrated, the loader is called internally and the HTML document is returned with a modified state. For the action, Remix waits for POST requests containing the form data and responds with the modified HTML document.

Fig. 6: Comparison of the processes of an action in a hydrated and non-hydrated application.

Progressive Enhancement

Through the data flow used, Remix allows developers to create websites so it works well without client-side JavaScript, but can also be enhanced through JavaScript. Besides data flow, this concept can be found in routing. After hydration, instead of a whole HTML document, only new data is requested as JSON and already preloaded before the page load thanks to the hover event.

This concept that JavaScript is not needed but improves the user experience is called Progressive Enhancement [2]. In my opinion, websites should use this concept wherever possible. Nothing is more confusing than a website that looks ready to load, but isn’t responsive to user interactions. Progressive Enhancement should also be considered especially because of trends towards increased mobile usage [3]. This is because websites take longer to load scripts, especially on smartphones, due to poorer wireless connections and weaker performance.

Remix and Next.js: A Comparison

In order to compare Remix and Next.js, you must first clarify which Next.js version or strategy is meant. As of version 13, Next.js offers a new routing system that resembles Remix’s routing system in many aspects. Before version 13, Next.js only offered “classic” routing. Next.js refers to this as the “Pages Router”. Pages could be placed in the pages folder, each defining the entire page. Next.js provides a similar system for routing pages. Instead of a loader, Next.js has getServerSideProps and getStaticProps. Both functions each define data that can be read and used in the render function via a function parameter.

There is no action like in Remix for Next.js page routing. You can define API routes that can be called in the render function with fetch, for example. Data flow is less integrated by the framework this way. Developers must take care of revalidation. This requires a separate API endpoint, because although Next.js automatically creates an API endpoint for getServerSideProps, the route URL is changed with a random seed in every build process.

Since version 13, there is new app routing. Instead of the pages folder, an app folder is used. Routing builds on nesting and, like Remix, it has benefits like parallelization, “data proximity”, less code duplication, etc. The new app routing also brings its own data concept with actions and revalidation, but it has a different syntax and approach.

Conclusion

Remix is a fresh React framework that specializes in server-side rendered websites. It tries to map the HTML5 API as closely as possible, building the data concept on top of the forms API. It’s a strategy so old that it feels new again. Despite the innovative concepts Remix brings to the table, it’s unlikely to prevail over Next.js as a framework for server-side rendered websites. Next.js is simply too widespread and established. There’s already a large and stable Next.js ecosystem of tooling, resources, libraries, and developers that Remix cannot compete with.

So, has Remix failed? No, quite the opposite! Even if it doesn’t catch on as the “go-to” framework, Remix illustrates the benefits of paradigms like nested routing and Progressive Enhancement. It has likely indirectly contributed to a better routing concept for Next.js and possibly other frameworks too.

Remix is constantly evolving and will surely bring more innovations. Currently, the Remix team is experimenting with a new routing declaration strategy that does away with subfolders altogether. Who knows, maybe this strategy will be found in other frameworks in the near future?

Links & References

[1] Remix.run – documentation page for the json helper function: https://remix.run/docs/en/1.16.1/utils/json

[2] MDN definition of “progressive enhancement”: https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement

[3] Desktop and mobile usage market shares from 2011 to 2022: https://gs.statcounter.com/platform-market-share/desktop-mobile/europe#yearly-2011-2022

[4] Twitter status of Lee Robinson: https://twitter.com/leeerob/status/1465702417513680897

The post Is Remix Better Than Next.js? appeared first on International JavaScript Conference.

]]>
What are React Server Components? https://javascript-conference.com/blog/what-are-react-server-components/ Tue, 09 Mar 2021 10:21:06 +0000 https://javascript-conference.com/?p=81722 The year 2020 ended with a bang for React developers when just before Christmas, Facebook's React team unveiled the RFC for React Server Components. Even though Facebook itself talks about React Server Components still being the dream of the future, let's take a look at the topic in this article. React Server Components could become the biggest innovation for React developers since the introduction of Hooks.

The post What are React Server Components? appeared first on International JavaScript Conference.

]]>
To answer the question of why you actually need React Server Components, it makes sense to backtrack a bit. I myself have been developing so-called single page applications (SPAs) for over 10 years. These are applications in which the user calls up an HTML page only once and further navigation takes place via JavaScript and reloaded data. At that time, the whole thing wasn’t called SPA but RIA (Rich Internet Applications), but the basic idea remains the same. These applications have gained popularity in the last 10 years. Often people go straight to Angular or React for new web applications, which have many advantages – but as always in life, these advantages go hand in hand with disadvantages. 

Disadvantages of SPAs

One of the disadvantages of single page applications is that they often cannot be indexed well by search engines, because they only deliver a small HTML framework without content and the rest is loaded by JavaScript. Search engines like Google are working on indexing such pages, but you should not rely on that. Additionally, SPAs tend to load huge amounts of JavaScript before the user can start using the application. This problem has been reduced due to faster and faster internet access. But when you are sitting on a train between Cologne and Düsseldorf, you will get annoyed after nothing happens for quite a while. The combination of both problems results in a user who waits for a minute for “the few megabytes of JavaScript” to download while looking at a blank page the whole time. Of course, no one wants to subject their own users to that. Since Google has also recognized that users do not like to wait, loading times are now also included in search engine ranking.

Server-side rendering as a solution?

Server-side rendering, or SSR for short, has established itself as a solution to the problem that search engines cannot index SPAs and users should not see a blank page during load time. The React community, in particular, has been talking about components and entire applications being “isomorphic” for several years. This is a technical mathematics term, but in this context, it only means: The component or application can be executed both server-side and in the browser. Typically, the server renders the components for display in the browser using Node.js. The resulting HTML skeleton is then sent to the client. Rendering there should be complete before the JavaScript code starts loading and the SPA becomes interactive. 

We can use server-side rendering to make sure that the blank page is not visible for so long, and search engines can index our page. Still, we have to download the complete JavaScript code, and strictly speaking, rendering that was done server-side is done again in the client.

React Server Components introduced

This is where React Server Components become an interesting concept. React Server Components allow us to completely offload individual components, along with their dependencies, to the server. This means they no longer run in the browser, and that means the amount of JavaScript that needs to be downloaded (and executed) shrinks. In Facebook’s initial experiments in a production environment, React Server Components resulted in a 29 percent smaller bundle size, or nearly a third, according to Facebook’s video presentation on React Server Components [1]. And that’s just the beginning.

Let’s take a look at an example of what this might look like. Let’s assume that we are working on a project that already supports React Server Components. Facebook provides a demo project for this purpose. Let’s start with a component that displays Markdown text as HTML. For this, we use the open source library marked in Listing 1.

Listing 1

import marked from 'marked';
 
const longMarkdownString = `/* Long Text */`;
export default function MarkdownExample() {
  return (
	

<div className="markdown" dangerouslySetInnerHTML={{ __html: marked(longMarkdownString), }} />
  );
}

The syntax with dangerouslySetInnerHTML is perhaps a bit unusual but has nothing to do with React Server Components, it simply ensures that we render HTML directly without any form of sanitation. Other than that, the component looks like a normal React component, and it is. It can be easily integrated into an existing React application and will run in the browser – including our dependency on marked. We can now turn the component into a React Server Component. We do that by renaming the file from MarkdownExample.js to MarkdownExample.server.js. Now the file is no longer executed in the browser, but on our server. As a result, the Markdown library is no longer needed in the browser – instead, the conversion from Markdown to HTML is done on the server-side. In the sample code, we simply put the Markdown string in a variable. Here, we could communicate with an API on the server-side instead. This doesn’t have to be accessible from our browser, but only from our server. We could also simply read the data from the server’s file system, or even from a server-side database. 

At this point, you may be asking where is the advantage compared to a REST API that provides our Markdown directly as HTML. While the question is valid, we will see that there are still advantages to using React Server Components.

In the context of React Server Components, which are identified by the filename suffix .server.js, there are also Client Components, with the .client.js suffix in the filename. All components that we have developed so far for the browser are also client components. But there is also a mixture of both, so-called shared components, which can run on the client and the server. 

For example, let’s imagine that we are developing a CMS that renders Markdown content. For the end users, we render the Markdown in a React Server Component and send it to the user ready rendered. An editor could also have an “edit” button displayed next to the content. When this button is clicked, the editor is offered a text field to edit. We can now use our Markdown component on the client-side and show the editor a preview of their content in real-time, without having to communicate with the server. However, we need to load the Markdown component with its dependency on the marked library the moment the editor clicks on “edit”, and not when the end user looks at the article. To achieve this, we need a bundler that supports that. The React team at Facebook is cooperating with the webpack team on this, and there is already a pre-release version of a plug-in that solves this problem for us. 

Let’s look at our MarkdownExample component again. Now, this can be used both as a React Server Component and as a Client Component. Since React Server Components and SSR are not mutually exclusive, we can also easily use this component in server-side rendering with NextJS or Frontastic, for example. 

You may still be asking if a simple API for converting Markdown to HTML wouldn’t work just as well. And of course, that’s the case for now. But for the editor, we would then have to rebuild the functionality in JavaScript or the preview would no longer be real-time. If we build the Markdown conversion once as an API and once as a React component, there is also a risk of the results diverging.

Avoiding waterfalls

Let’s move on to another positive effect that can result from using React Server Components. In React applications without Server Components, there is sometimes a so-called “waterfall” in the browser. Let’s imagine a component like the following, which represents an article on javascript-conference.com: <Article articleId={4711} />. This component is apparently responsible for loading the article data itself because it only gets passed the article ID. It represents the article, including the comments. The component’s code might look like the one shown in Listing 2.

Listing 2

const Article = ({ articleId }) => {
  const [article, setArticle] = useState(null);
 
  useEffect(() => {
    fetchArticle(articleId).then((articleData) => {
      setArticle(articleData);
	});
  }, [articleId]);
 
  if (article === null) {
	return <LoadingSpinner />;
  }
 
  return (
	<>
      <ArticleDetails article={article} />
      <ArticleComments article={article} />
	</>
  );
};

The component uses a reusable hook to load an article. Since we probably don’t load all the comments when we load an article, the ArticleComments component itself has to load the comments again. Now, let’s take a look at what happens when our Article component is rendered.

In the first rendering, article is initialized with null. The effect specified in useEffect is not executed directly during rendering. This means that article is always null in the first rendering. So, the component always renders a LoadingSpinner in the first rendering. After rendering, the effect is executed, which loads the data from the server and sets the article in the state asynchronously after successful loading. By changing the state, a new rendering is executed. In this rendering, the article is no longer null, and we render the ArticleDetails and ArticleComments components. If the ArticleDetails component loads information about the author of the article and the ArticleComments component starts loading the comments, we have our waterfall – an unwanted sequential loading of data that we would have liked to load in parallel. The page builds up piece by piece, and content may jump back and forth as new data becomes available.

Now let’s imagine that we use exactly the same component as a Server Component. Please  not that this won’t really work like that, it’s just an example (more on that later). We would have to adjust the component a bit, but conceptually it remains very similar, and so I’ll spare us this digression. Server-side we would have a waterfall where first the article data is loaded and then the comments and auto data. On the server-side, however, this is usually a much smaller problem, as the lower latency means that the individual calls can be completed much more quickly. The problem of content being displayed bit by bit disappears completely when done server-side.

There are, of course, other ways to get rid of such waterfalls. However, those often involve a certain change in the architecture of the React application, which is not to everyone’s liking. The way shown above is sometimes called “fetch-on-render” and is relatively popular and widely used. In that respect, it’s nice to see that React Server Components can shrink the problem.

The downsides of React Server Components

As always in life, React Server Components is not a magical solution, and it’s important to look at its downsides.

Let’s start with the obvious: React Server Components are just a proposal so far. They are the result of lengthy research at Facebook, and the answer to several problems Facebook has had. The team at Facebook is optimistic that this new approach can solve problems for many applications. There is no release date yet for when we can start using React Server Components in our applications. The React team currently assumes that React Server Components will first find their way into frameworks like NextJS, and direct use is even further away. But there is no release date for that either.

Also, it’s not like any of our existing components will automatically work as a React Server Component. For example, some hooks do not work server-side; for example, useState or useEffect are not supported in React Server Components. That’s why our Article component above doesn’t work as a React Server Component. However, we can implement the same functionality as a React Server Component, just without these hooks. By the way, this restriction also implies that any (custom) hooks that use one of the unsupported hooks cannot be used in React Server Components.

The same is true the other way around – not all React Server Components can also be executed in the browser. This is partly the case because React Server Components can access APIs that are not available client-side. For example, in React Server Components we can access the server’s file system. Fortunately, we can’t do that from the browser.

Another limitation is that Server Components may render Client Components (and native HTML elements), but Client Components may not render Server Components. However, it is possible to pass rendered React Server Components from a React Server Component as a prop to a client component, for example, as children.

By the way, it is interesting to understand that when we use a Client Component inside a Server Component, the rendering of the Client Component takes place regularly on the client. If the Server Component wants to send new data to the Client Component, this data is transferred via http and given to the Client Component – which then renders itself again, but does not lose its state.

Conclusion and outlook

Server Components could actually accelerate the spread of React once again, as the framework can also be used in applications where it was previously not possible. They also allow frontend developers, who previously had little insight into the backend, to develop parts of the backend in ways they are familiar with. This is a hurdle that fullstack developers often lose sight of. Nevertheless, we are moving in a direction here that allows frontend developers to be able to take on more responsibility for the complete user interface.

The ability to run the same code server-side or client-side depending on the situation is something that I think is a very exciting concept. It allows for interesting options, such as more complex algorithms that need lots of training data to be added. The training data is sometimes very large and makes the downloaded JavaScript look small. For example, one could run the algorithm server-side while the training data is still being loaded. Only when everything is available offline in the browser would the calculation then be outsourced to the client, which should improve performance and save computing capacity on the server-side, and possibly even make our application work offline.

Basically, of course, this is not a revolutionary new development. Many of the “advantages” gained through Server Components are inherent in a traditional web application without JavaScript in the browser. Facebook also explicitly says that they have been inspired by such traditional web architectures. Nevertheless – React Server Components allow us to get the best of both worlds. That’s why I’m very excited to experience React Server Components in a real application sometime in the future.

Links & Literature

[1] https://www.youtube.com/watch?v=TQQPAU21ZUw

The post What are React Server Components? appeared first on International JavaScript Conference.

]]>