Skip to content

Commit

Permalink
docs(rendering): improve rendering modes section
Browse files Browse the repository at this point in the history
  • Loading branch information
Atinux committed May 18, 2023
1 parent 4d0c9bf commit 244c681
Showing 1 changed file with 83 additions and 68 deletions.
151 changes: 83 additions & 68 deletions docs/2.guide/1.concepts/3.rendering.md
@@ -1,40 +1,20 @@
---
description: "Nuxt supports different rendering modes. Each one has pros and cons covered in this section."
description: "Nuxt supports different rendering modes, univeral rendering, client-side rendering but also offers hybrid-rendering and the possibility to render on CDN Edge Servers."
---

# Rendering Modes

Both the browser and server can interpret JavaScript code to render Vue.js components into HTML elements. This step is called **rendering**. Nuxt supports both **client-side** and **universal** rendering. The two approaches have pros and cons that we will cover in this section.
Nuxt supports different rendering modes, [universal rendering](#universal-rendering), [client-side rendering](#client-side-rendering) but also offers [hybrid-rendering](#hybrid-rendering) and the possibility to render your application on [CDN Edge Servers](#edge-side-rendering).

## Client-side Only Rendering
Both the browser and server can interpret JavaScript code to render Vue.js components into HTML elements. This step is called **rendering**. Nuxt supports both **universal** and **client-side** rendering. The two approaches have benefits and downsides that we will cover.

Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface.

![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/light/csr.svg){.light-img.dark:hidden}
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/dark/csr.svg){.dark-img.hidden.dark:block}

While this technique allows building complex and dynamic UIs with smooth page transitions, it has different pros and cons:

### Pros

- **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object.
- **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host Client-only applications on any static server with HTML, CSS, and JavaScript files.
- **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable.

### Cons

- **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience.
- **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering.

### Examples

Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**.
By default, Nuxt uses **universal rendering** to provide better user experience, performance and to optimize search engine indexing, but you can switch rendering modes in [one line of configuration](/docs/api/configuration/nuxt-config#ssr).

## Universal Rendering

When the browser requests a URL with universal (client-side + server-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
When the browser requests a URL with universal (server-side + client-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.

To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client loads the javascript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client (browser) loads the JavaScript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.

Making a static page interactive in the browser is called "Hydration."

Expand All @@ -43,15 +23,15 @@ Universal rendering allows a Nuxt application to provide quick page load times w
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/light/ssr.svg){.light-img.dark:hidden}
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/dark/ssr.svg){.dark-img.hidden.dark:block}

### Pros

**Benefits of server-side rendering:**
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated one. At the same time, Nuxt preserves the interactivity of a web application when the hydration process happens.
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.

### Cons

**Downsides of server-side rendering:**
- **Development constraints:** Server and browser environments don't provide the same APIs, and it can be tricky to write code that can run on both sides seamlessly. Fortunately, Nuxt provides guidelines and specific variables to help you determine where a piece of code is executed.
- **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation.
- **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation. A cost reduction is possible by leveraging [edge-side-rendering](#edge-side-rendering).

Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.**

::alert
For more examples about writing Vue code without hydration mismatch, see [the Vue docs](https://vuejs.org/guide/scaling-up/ssr.html#hydration-mismatch).
Expand All @@ -61,38 +41,64 @@ For more examples about writing Vue code without hydration mismatch, see [the Vu
When importing a library that relies on browser APIs and has side effects, make sure the component importing it is only called client-side. Bundlers do not treeshake imports of modules containing side effects.
::

### Examples

Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.**
## Client-Side Rendering

## Summary
Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface.

Client-side and universal rendering are different strategies to display an interface in a browser.
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/light/csr.svg){.light-img.dark:hidden}
![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](/assets/docs/concepts/rendering/dark/csr.svg){.dark-img.hidden.dark:block}

By default, Nuxt uses **universal rendering** to provide better user experience and performance, and to optimize search engine indexing, but you can switch rendering modes in [one line of configuration](/docs/api/configuration/nuxt-config#ssr).
**Benefits of client-side rendering:**
- **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object.
- **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host Client-only applications on any static server with HTML, CSS, and JavaScript files.
- **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable.

## New Rendering Patterns in Nuxt 3
**Downsides of client-side rendering:**
- **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience.
- **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering.

In most cases, universal rendering as performed in Nuxt 2 offers a good user and developer experience. However, Nuxt 3 takes universal rendering a step further by introducing hybrid rendering and edge-side rendering.
Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**.

### Hybrid Rendering
You can enable client-side only rendering with Nuxt in your `nuxt.config.ts`:

Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ssr: false
})
```

### Rendering on CDN Edge Workers
## Hybrid Rendering

Traditionally, server-side and universal rendering was only possible using Node.js. Nuxt 3 takes it to another level by directly rendering code in CDN edge workers, reducing latency and costs.
Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL.

Nitro is the new [server engine](/docs/guide/concepts/server-engine) that powers Nuxt 3. It offers cross-platform support for Node.js, Deno, Workers, and more. Nitro's design is platform-agnostic and allows rendering a Nuxt application at the edge, closer to your users, allowing replication and further optimizations.
Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.

### Route Rules
Nuxt 3 includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route!

> 🧪 Route rules are still under active development, and subject to change.
Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.unjs.io/guide/cache).

Previously every route/page of a Nuxt application and server must use the same rendering mode, client-side or universal. But in various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.
**Example:**

Nuxt 3 includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route! Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [nitro caching layer](https://nitro.unjs.io/guide/cache). Whenever possible, route rules will be automatically applied to the deployment platform's native rules (currently Netlify and Vercel are supported).
```ts [nuxt.config.ts]
export default defineNuxtConfig({
routeRules: {
// Homepage pre-rendered at build time
'/': { prerender: true },
// Product page generated on-demand, revalidates in background
'/products/**': { swr: true },
// Blog post generated on-demand once until next deploy
'/blog/**': { isr: true },
// Admin dashboard renders only on client-side
'/admin/**': { ssr: false },
// Add cors headers on API routes
'/api/**': { cors: true },
// Redirects legacy urls
'/old-page': { redirect: '/new-page' }
}
})
```

The different properties you can use are the following:
- `redirect` - Define server-side redirects.
- `ssr` - Disables server-side rendering for sections of your app and make them SPA-only with `ssr: false`
- `cors` - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers`
Expand All @@ -102,24 +108,33 @@ Nuxt 3 includes route rules and hybrid rendering support. Using route rules you
- `prerender` - Prerenders routes at build time and includes them in your build as static assets
- `experimentalNoScripts` - Disables rendering of Nuxt scripts and JS resource hints for sections of your site.

**Examples:**
Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported).

```ts
export default defineNuxtConfig({
routeRules: {
// Static page generated on-demand, revalidates in background
'/blog/**': { swr: true },
// Static page generated on-demand once
'/articles/**': { static: true },
// Set custom headers matching paths
'/_nuxt/**': { headers: { 'cache-control': 's-maxage=0' } },
// Render these routes with SPA
'/admin/**': { ssr: false },
// Add cors headers
'/api/v1/**': { cors: true },
// Add redirect headers
'/old-page': { redirect: '/new-page' },
'/old-page2': { redirect: { to: '/new-page', statusCode: 302 } }
}
})
```

::alert{type="warning"}
Note that Hybrid Rendering is not available when using `nuxt generate`.
::

## Edge-Side Rendering

Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt 3 that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience.

With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode.

When a request for a page is made, instead of going all the way to the original server, it's intercepted by the nearest edge server. This server generates the HTML for the page and sends it back to the user. This process minimizes the physical distance the data has to travel, **reducing latency and loading the page faster**.

Edge-side rendering is possible thanks to [Nitro](https://nitro.unjs.io), the [server engine](/docs/guide/concepts/server-engine) that powers Nuxt 3. It offers cross-platform support for Node.js, Deno, CloudFlare Workers, and more.

The current platforms where you can leverage ESR are:
- [CloudFlare Pages](https://pages.cloudflare.com/) with zero configuration using the git integration and the `nuxt build` command
- [Lagon](https://lagon.app) using the `NITRO_PRESET=lagon npx nuxt build` command
- [Vercel Edge Functions](https://vercel.com/features/edge-functions) using the `nuxt build` command and `NITRO_PRESET=vercel-edge` environment variable
- [Netlify Edge Functions](https://www.netlify.com/products/#netlify-edge-functions) using the `nuxt build` command and `NITRO_PRESET=netlify-edge` environment variable

Note that **Hybrid Rendering** can be used when using Edge-Side Rendering with route rules.

You can explore open source examples deployed on some of the platform mentioned above:
- [Nuxt Todos Edge](https://github.com/atinux/nuxt-todos-edge): A todos application with user authentication, SSR and SQLite.
- [Atinotes](https://github.com/atinux/atinotes): An editable website with universal rendering.

<!-- TODO: link to templates with ESR category for examples -->

0 comments on commit 244c681

Please sign in to comment.