Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: further clarify and reinforce the performance impacts of single page apps (SPAs) #11637

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

benmccann
Copy link
Member

@benmccann benmccann commented Jan 14, 2024

A huge portion of SvelteKit apps in the wild that are failing core web vitals are single page apps. The percentage of SvelteKit apps which pass/fail are tracked on cwvtech.report leveraging data from HTTP Archive. Some folks at Google were able to supply me with a random sample of SvelteKit sites from this dataset. While investigating the failing sites, I saw that the most common cause of failure was extra round trips due to deployment as a SPA.

Copy link

changeset-bot bot commented Jan 14, 2024

⚠️ No Changeset found

Latest commit: 96a9889

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@eltigerchino eltigerchino added the documentation Improvements or additions to documentation label Jan 15, 2024
@benmccann benmccann changed the title docs: clarify that fallback page is generated docs: clarify the harms of single page app (SPA) mode Jan 15, 2024
@benmccann benmccann changed the title docs: clarify the harms of single page app (SPA) mode docs: further clarify and reinfornce the harms of single page app (SPA) mode Jan 15, 2024
@benmccann benmccann changed the title docs: further clarify and reinfornce the harms of single page app (SPA) mode docs: further clarify and reinforce the harms of single page app (SPA) mode Jan 15, 2024
@benmccann benmccann changed the title docs: further clarify and reinforce the harms of single page app (SPA) mode docs: further clarify and reinforce the harms of single page apps (SPAs) Jan 15, 2024
Copy link
Member

@dummdidumm dummdidumm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gotta push back on the language of these changes and some of these changes in general - it sounds like we're on a crusade against SPA mode now. This creates FUD ("omg SPA mode always bad", which isn't true because it depends on your site) and may turn people off from using SvelteKit ("ok so they hate SPA I'll look for another framework that supports it better") which helps neither web vitals nor us.

I think there are other avenues to explore:

  • some kind of SvelteKit dev tools which somehow analyzes your page and gives you some tips what you can improve, where this plays a part
  • partial prerendering which will improve perceived performance and probably web vitals
  • understanding why people opt for SPA mode in the first place and solve the roadblocks towards leaving SSR enabled

documentation/docs/40-best-practices/05-performance.md Outdated Show resolved Hide resolved
documentation/docs/40-best-practices/20-seo.md Outdated Show resolved Hide resolved
documentation/docs/60-appendix/60-glossary.md Outdated Show resolved Hide resolved
@@ -46,6 +46,6 @@ In SvelteKit, you can do static site generation by using [`adapter-static`](adap

## SSR

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)).
Server-side rendering (SSR) is the generation of the page contents on the server. Returning the page contents from the server via SSR (including with prerendering) is highly preferred for performance and SEO. It drastically improves performance by avoiding the introduction of an extra round trip 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really true - it's more about the perceived performance. The HTML is already there, but you still have the second roundtrip of fetching the JS which hydrates your stuff and then it's interactive. If your page doesn't work without JS it might feel just as bad.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'roundtrip' refers to the data that is inlined into the page (whether in the form of serialised server data or inlined event.fetch responses)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And if your site is content to be read, it's much more than perceived performance - it's actual performance

@benmccann
Copy link
Member Author

I pushed an update to the docs to recommend using a name other than index.html for the fallback. See #11661

@@ -70,7 +72,7 @@ The directory to write static assets (the contents of `static`, plus client-side

### fallback

Specify a fallback page for [SPA mode](single-page-apps), e.g. `index.html` or `200.html` or `404.html`.
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. Note that this option has large negative performance and SEO impacts and is recommended only in very limited circumstances such as when being wrapped in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives.
Copy link
Member

@eltigerchino eltigerchino Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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. Note that this option has large negative performance and SEO impacts and is recommended only in very limited circumstances such as when being wrapped in a mobile app. See the [single page apps](single-page-apps) documentation for more details and alternatives.
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.
> 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.

@@ -2,20 +2,21 @@
title: Single-page apps
---

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:
You can turn a SvelteKit app into a fully client-rendered single-page app (SPA) by specifying a _fallback page_ that will be served for any URLs that can't be served via another means such as by returning a prerendered page.
Copy link
Member

@eltigerchino eltigerchino Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You can turn a SvelteKit app into a fully client-rendered single-page app (SPA) by specifying a _fallback page_ that will be served for any URLs that can't be served via another means such as by returning a prerendered page.
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.


> SPA mode has a large negative performance impact by forcing two network round trips before rendering can begin. This may be acceptable if you are serving an application from the local network (e.g. a mobile app that wraps a locally-served SPA), but probably is not for most websites on the internet 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), 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)).
>
> You can avoid these drawbacks on a given page by [prerendering it](#prerendering-individual-pages). You should thus prerender 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. If you cannot, you should strongly consider using an adapter which supports server side rendering — SvelteKit offers officially supported adapters for multiple different providers that offer free SSR hosting for sites below a certain threshold.
Copy link
Member

@eltigerchino eltigerchino Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> You can avoid these drawbacks on a given page by [prerendering it](#prerendering-individual-pages). You should thus prerender 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. If you cannot, you should strongly consider using an adapter which supports server side rendering — SvelteKit offers officially supported adapters for multiple different providers that offer free SSR hosting for sites below a certain threshold.
> You can avoid these drawbacks on a given page by [prerendering it](#prerendering-individual-pages). You should thus prerender 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 that offer free SSR hosting for sites below a certain threshold.


## Usage

First, disable SSR for the pages you don't want to prerender. These pages will be seved via a 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.
Copy link
Member

@eltigerchino eltigerchino Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
First, disable SSR for the pages you don't want to prerender. These pages will be seved via a 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.
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.

- 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 an extra network roundtrip before a single pixel can be displayed.

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.
- Avoid this issue by using [server `load` functions](/docs/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 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Avoid this issue by using [server `load` functions](/docs/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 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.
- Avoid this issue by using [server `load` functions](/docs/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 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.

@@ -10,6 +10,10 @@ Client-side rendering (CSR) is the generation of the page contents in the web br

In SvelteKit, client-side rendering will be used by default, but you can turn off JavaScript with [the `csr = false` page option](page-options#csr).

## Hybrid app

SvelteKit uses a hybrid rendering mode by default where it loads the initial HTML from the server (SSR) and the updates the page contents on subsequent navigations via client-side rendering (CSR).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SvelteKit uses a hybrid rendering mode by default where it loads the initial HTML from the server (SSR) and the updates the page contents on subsequent navigations via client-side rendering (CSR).
SvelteKit uses a hybrid rendering mode by default where it loads the initial HTML from the server (SSR), then updates the page contents on subsequent navigations via client-side rendering (CSR).

@@ -34,9 +38,9 @@ In SvelteKit, client-side routing will be used by default, but you can skip it w

## SPA

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, which has the shortcoming described above. 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.
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
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.

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

## SSR

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)).
Server-side rendering (SSR) is the generation of the page contents on the server. Returning the page contents from the server via SSR (including with prerendering) is highly preferred for performance and SEO. It drastically improves performance by avoiding the introduction of an extra round trip 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.
Copy link
Member

@eltigerchino eltigerchino Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Server-side rendering (SSR) is the generation of the page contents on the server. Returning the page contents from the server via SSR (including with prerendering) is highly preferred for performance and SEO. It drastically improves performance by avoiding the introduction of an extra round trip 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.
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 drastically improves performance by avoiding the introduction of an extra round trip 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants