Skip to content

Commit 359caf7

Browse files
benmccannghostdevvdummdidummeltigerchinoRich-Harris
authored
docs: further clarify and reinforce the performance impacts of single page apps (SPAs) (#11637)
* docs: clarify that fallback page is generated * Update documentation/docs/25-build-and-deploy/50-adapter-static.md Co-authored-by: Willow (GHOST) <[email protected]> * Update documentation/docs/25-build-and-deploy/50-adapter-static.md * try to do a better job scaring users away from spa mode * tweak * update glossary * Update documentation/docs/40-best-practices/05-performance.md Co-authored-by: Simon H <[email protected]> * update aside * update SEO wording * update glossary to differentiate between SPAs used publicly vs internally * address feedback * Update documentation/docs/25-build-and-deploy/50-adapter-static.md Co-authored-by: Simon H <[email protected]> * clarify spa vs hybrid app * clarifications. recommend a page name other than index.html to avoid conflicting with prerendering * try to shrink/tighten up content * Update documentation/docs/40-best-practices/05-performance.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/40-best-practices/05-performance.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/25-build-and-deploy/50-adapter-static.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/25-build-and-deploy/55-single-page-apps.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/25-build-and-deploy/55-single-page-apps.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/25-build-and-deploy/55-single-page-apps.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/40-best-practices/05-performance.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/60-appendix/60-glossary.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/60-appendix/60-glossary.md Co-authored-by: Tee Ming <[email protected]> * Update documentation/docs/60-appendix/60-glossary.md Co-authored-by: Tee Ming <[email protected]> * tweaks * tweaks * tweak * fix * gah whoops * gah * ffffuuuuu * more * fml * godDAMNIT --------- Co-authored-by: Willow (GHOST) <[email protected]> Co-authored-by: Simon H <[email protected]> Co-authored-by: Tee Ming <[email protected]> Co-authored-by: Chew Tee Ming <[email protected]> Co-authored-by: Rich Harris <[email protected]>
1 parent 20c804c commit 359caf7

File tree

5 files changed

+43
-29
lines changed

5 files changed

+43
-29
lines changed

documentation/docs/25-build-and-deploy/50-adapter-static.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ export default config;
3636

3737
```js
3838
/// file: src/routes/+layout.js
39-
// This can be false if you're using a fallback (i.e. SPA mode)
39+
// If you're using a fallback (i.e. SPA mode) you don't need to prerender all
40+
// pages by setting this here, but should prerender as many as possible to
41+
// avoid large performance and SEO impacts
4042
export const prerender = true;
4143
```
4244

@@ -76,7 +78,9 @@ The directory to write static assets (the contents of `static`, plus client-side
7678

7779
### fallback
7880

79-
Specify a fallback page for [SPA mode](single-page-apps), e.g. `index.html` or `200.html` or `404.html`.
81+
To create a [single page app (SPA)](single-page-apps) you must specify the name of the fallback page to be generated by SvelteKit, which is used as the entry point for URLs that have not been prerendered. This is commonly `200.html`, but can vary depending on your deployment platform. You should avoid `index.html` where possible to avoid conflicting with a prerendered homepage.
82+
83+
> This option has large negative performance and SEO impacts. It is only recommended in certain circumstances such as wrapping the site in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives.
8084
8185
### precompress
8286

documentation/docs/25-build-and-deploy/55-single-page-apps.md

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@
22
title: Single-page apps
33
---
44

5-
You can turn any SvelteKit app, using any adapter, into a fully client-rendered single-page app (SPA) by disabling SSR at the root layout:
5+
You can turn a SvelteKit app into a fully client-rendered single-page app (SPA) by specifying a _fallback page_. This page will be served for any URLs that can't be served by other means such as returning a prerendered page.
66

7+
> [!NOTE] SPA mode has a large negative performance impact by forcing multiple network round trips (for the blank HTML document, then for the JavaScript, and then for any data needed for the page) before content can be shown. Unless you are serving the app from a local network (e.g.a mobile app that wraps a locally-served SPA) this will delay startup, especially when considering the latency of mobile devices. It also harms SEO by often causing sites to be downranked for performance (SPAs are much more likely to fail [Core Web Vitals](https://web.dev/explore/learn-core-web-vitals)), excluding search engines that don't render JS, and causing your site to receive less frequent updates from those that do. And finally, it makes your app inaccessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)).
8+
>
9+
> You can avoid these drawbacks by [prerendering](#Prerendering-individual-pages) as many pages as possible when using SPA mode (especially your homepage). If you can prerender all pages, you can simply use [static site generation](adapter-static) rather than a SPA. Otherwise, you should strongly consider using an adapter which supports server side rendering. SvelteKit has officially supported adapters for various providers with generous free tiers.
10+
11+
## Usage
12+
13+
First, disable SSR for the pages you don't want to prerender. These pages will be served via the fallback page. E.g. to serve all pages via the fallback by default, you can update the root layout as shown below. You should [opt back into prerendering individual pages and directories](#Prerendering-individual-pages) where possible.
714
```js
815
/// file: src/routes/+layout.js
916
export const ssr = false;
1017
```
1118

12-
> [!NOTE] In most situations this is not recommended: it harms SEO, tends to slow down perceived performance, and makes your app inaccessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)).
13-
14-
If you don't have any server-side logic (i.e. `+page.server.js`, `+layout.server.js` or `+server.js` files) you can use [`adapter-static`](adapter-static) to create your SPA by adding a _fallback page_.
15-
16-
## Usage
17-
18-
Install with `npm i -D @sveltejs/adapter-static`, then add the adapter to your `svelte.config.js` with the following options:
19+
If you don't have any server-side logic (i.e. `+page.server.js`, `+layout.server.js` or `+server.js` files) you can use [`adapter-static`](adapter-static) to create your SPA. Install `adapter-static` with `npm i -D @sveltejs/adapter-static` and add it to your `svelte.config.js` with the `fallback` option:
1920

2021
```js
2122
/// file: svelte.config.js
@@ -35,10 +36,22 @@ export default config;
3536

3637
The `fallback` page is an HTML page created by SvelteKit from your page template (e.g. `app.html`) that loads your app and navigates to the correct route. For example [Surge](https://surge.sh/help/adding-a-200-page-for-client-side-routing), a static web host, lets you add a `200.html` file that will handle any requests that don't correspond to static assets or prerendered pages.
3738

38-
On some hosts it may be `index.html` or something else entirely — consult your platform's documentation.
39+
On some hosts it may be something else entirely — consult your platform's documentation. We recommend avoiding `index.html` if possible as it may conflict with prerendering.
3940

4041
> [!NOTE] Note that the fallback page will always contain absolute asset paths (i.e. beginning with `/` rather than `.`) regardless of the value of [`paths.relative`](configuration#paths), since it is used to respond to requests for arbitrary paths.
4142
43+
## Prerendering individual pages
44+
45+
If you want certain pages to be prerendered, you can re-enable `ssr` alongside `prerender` for just those parts of your app:
46+
47+
```js
48+
/// file: src/routes/my-prerendered-page/+page.js
49+
export const prerender = true;
50+
export const ssr = true;
51+
```
52+
53+
You won't need a Node server or server capable of running JavaScript to deploy this page. It will only server render your page while building your project for the purposes of outputting an `.html` page that can be served from any static web host.
54+
4255
## Apache
4356

4457
To run an SPA on [Apache](https://httpd.apache.org/), you should add a `static/.htaccess` file to route requests to the fallback page:
@@ -53,13 +66,3 @@ To run an SPA on [Apache](https://httpd.apache.org/), you should add a `static/.
5366
RewriteRule . /200.html [L]
5467
</IfModule>
5568
```
56-
57-
## Prerendering individual pages
58-
59-
If you want certain pages to be prerendered, you can re-enable `ssr` alongside `prerender` for just those parts of your app:
60-
61-
```js
62-
/// file: src/routes/my-prerendered-page/+page.js
63-
export const prerender = true;
64-
export const ssr = true;
65-
```

documentation/docs/40-best-practices/05-performance.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,14 @@ For slow-loading data that isn't needed immediately, the object returned from yo
8585

8686
### Preventing waterfalls
8787

88-
One of the biggest performance killers is what is referred to as a _waterfall_, which is a series of requests that is made sequentially. This can happen on the server or in the browser.
88+
One of the biggest performance killers is what is referred to as a _waterfall_, which is a series of requests that is made sequentially. This can happen on the server or in the browser, but is especially costly when dealing with data that has to travel further or across slower networks, such as a mobile user making a call to a distant server.
8989

90-
- Asset waterfalls can occur in the browser when your HTML requests JS which requests CSS which requests a background image and web font. SvelteKit will largely solve this class of problems for you by adding [`modulepreload`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/modulepreload) tags or headers, but you should view [the network tab in your devtools](#Diagnosing-issues) to check whether additional resources need to be preloaded. Pay special attention to this if you use web [fonts](#Optimizing-assets-Fonts) since they need to be handled manually.
91-
- If a universal `load` function makes an API call to fetch the current user, then uses the details from that response to fetch a list of saved items, and then uses _that_ response to fetch the details for each item, the browser will end up making multiple sequential requests. This is deadly for performance, especially for users that are physically located far from your backend. Avoid this issue by using [server `load` functions](load#Universal-vs-server) where possible.
92-
- Server `load` functions are also not immune to waterfalls (though they are much less costly since they rarely involve roundtrips with high latency). For example if you query a database to get the current user and then use that data to make a second query for a list of saved items, it will typically be more performant to issue a single query with a database join.
90+
In the browser, waterfalls can occur when your HTML kicks off request chains such as requesting JS which requests CSS which requests a background image and web font. SvelteKit will largely solve this class of problems for you by adding [`modulepreload`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/modulepreload) tags or headers, but you should view [the network tab in your devtools](#Diagnosing-issues) to check whether additional resources need to be preloaded.
91+
- Pay special attention to this if you use [web fonts](#Optimizing-assets-Fonts) since they need to be handled manually.
92+
- Enabling [single page app (SPA) mode](single-page-apps) will cause such waterfalls. With SPA mode, an empty page is generated, which fetches JavaScript, which ultimately loads and renders the page. This results in extra network round trips before a single pixel can be displayed.
93+
94+
Waterfalls can also occur on calls to the backend whether made from the browser or server. E.g. if a universal `load` function makes an API call to fetch the current user, then uses the details from that response to fetch a list of saved items, and then uses _that_ response to fetch the details for each item, the browser will end up making multiple sequential requests. This is deadly for performance, especially for users that are physically located far from your backend.
95+
- Avoid this issue by using [server `load` functions](load#Universal-vs-server) to make requests to backend services that are dependencies from the server rather than from the browser. Note, however, that server `load` functions are also not immune to waterfalls (though they are much less costly since they rarely involve round trips with high latency). For example, if you query a database to get the current user and then use that data to make a second query for a list of saved items, it will typically be more performant to issue a single query with a database join.
9396

9497
## Hosting
9598

documentation/docs/40-best-practices/20-seo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ While search engines have got better in recent years at indexing content that wa
1414
1515
### Performance
1616

17-
Signals such as [Core Web Vitals](https://web.dev/vitals/#core-web-vitals) impact search engine ranking. Because Svelte and SvelteKit introduce minimal overhead, it's easier to build high performance sites. You can test your site's performance using Google's [PageSpeed Insights](https://pagespeed.web.dev/) or [Lighthouse](https://developers.google.com/web/tools/lighthouse). Read [the performance page](performance) for more details.
17+
Signals such as [Core Web Vitals](https://web.dev/vitals/#core-web-vitals) impact search engine ranking. Because Svelte and SvelteKit introduce minimal overhead, they make it easier to build high performance sites. You can test your site's performance using Google's [PageSpeed Insights](https://pagespeed.web.dev/) or [Lighthouse](https://developers.google.com/web/tools/lighthouse). With just a few key actions like using SvelteKit's default [hybrid rendering](glossary#Hybrid-app) mode and [optimizing your images](images), you can greatly improve your site's speed. Read [the performance page](performance) for more details.
1818

1919
### Normalized URLs
2020

documentation/docs/60-appendix/60-glossary.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ In SvelteKit, client-side rendering will be used by default, but you can turn of
1414

1515
Rendering on the edge refers to rendering an application in a content delivery network (CDN) near the user. Edge rendering allows the request and response for a page to travel a shorter distance thus improving latency.
1616

17+
## Hybrid app
18+
19+
SvelteKit uses a hybrid rendering mode by default where it loads the initial HTML from the server (SSR), and then updates the page contents on subsequent navigations via client-side rendering (CSR).
20+
1721
## Hydration
1822

1923
Svelte components store some state and update the DOM when the state is updated. When fetching data during SSR, by default SvelteKit will store this data and transmit it to the client along with the server-rendered HTML. The components can then be initialized on the client with that data without having to call the same API endpoints again. Svelte will then check that the DOM is in the expected state and attach event listeners in a process called hydration. Once the components are fully hydrated, they can react to changes to their properties just like any newly created Svelte component.
@@ -50,9 +54,9 @@ In SvelteKit, client-side routing will be used by default, but you can skip it w
5054

5155
## SPA
5256

53-
A single-page app (SPA) is an application in which all requests to the server load a single HTML file which then does client-side rendering of the requested contents based on the requested URL. All navigation is handled on the client-side in a process called client-side routing with per-page contents being updated and common layout elements remaining largely unchanged. SPAs do not provide SSR and thus have worse performance and SEO characteristics. However, some applications are not greatly impacted by these shortcomings such as a complex business application behind a login where SEO would not be important and it is known that users will be accessing the application from a consistent computing environment.
57+
A single-page app (SPA) is an application in which all requests to the server load a single HTML file which then does client-side rendering based on the requested URL. All navigation is handled on the client-side in a process called client-side routing with per-page contents being updated and common layout elements remaining largely unchanged. Throughout this site, when we refer to a SPA, we use this definition where a SPA simply serves an empty shell on the initial request. It should not be confused with a [hybrid app](#Hybrid-app), which serves HTML on the initial request. It has a large performance impact by forcing two network round trips before rendering can begin. Because SPA mode has large negative performance and SEO impacts, it is recommended only in very limited circumstances such as when being wrapped in a mobile app.
5458

55-
In SvelteKit, you can [build an SPA with `adapter-static`](single-page-apps).
59+
In SvelteKit, you can [build SPAs with `adapter-static`](single-page-apps).
5660

5761
## SSG
5862

@@ -62,6 +66,6 @@ In SvelteKit, you can do static site generation by using [`adapter-static`](adap
6266

6367
## SSR
6468

65-
Server-side rendering (SSR) is the generation of the page contents on the server. SSR is generally preferred for SEO. While some search engines can index content that is dynamically generated on the client-side it may take longer even in these cases. It also tends to improve perceived performance and makes your app accessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)).
69+
Server-side rendering (SSR) is the generation of the page contents on the server. Returning the page contents from the server via SSR or prerendering is highly preferred for performance and SEO. It significantly improves performance by avoiding the introduction of extra round trips necessary in a SPA, and makes your app accessible to users if JavaScript fails or is disabled (which happens [more often than you probably think](https://kryogenix.org/code/browser/everyonehasjs.html)). While some search engines can index content that is dynamically generated on the client-side, it is likely to take longer even in these cases.
6670

6771
In SvelteKit, pages are server-side rendered by default. You can disable SSR with [the `ssr` page option](page-options#ssr).

0 commit comments

Comments
 (0)