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

Add option to prefetch links by default #790

Closed
Nick-Mazuk opened this issue Mar 31, 2021 · 15 comments · Fixed by #6442
Closed

Add option to prefetch links by default #790

Nick-Mazuk opened this issue Mar 31, 2021 · 15 comments · Fixed by #6442
Labels
breaking change feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. prefetch router
Milestone

Comments

@Nick-Mazuk
Copy link
Contributor

Nick-Mazuk commented Mar 31, 2021

Is your feature request related to a problem? Please describe.

There should be an option to prefetch links by default. In my opinion, page transitions should always be instantaneous if possible. And this is the default in other frameworks like Next.js and Gatsby.

Now you could just add sveltekit:prefetch to every single <a> tag, but there are some reasons why this isn't practical/possible.

  • It's annoying
  • You can easily forget some
  • If you're parsing markdown, you may not be able to add the prefetch attribute
  • When dealing with user-generated content (purely on the browser), again, it may not be possible to add the attribute
  • If you're using 3rd-party components, you cannot add the attribute

And not only are faster transitions better user experience, in some industries (like eCommerce), the extra 150-500 milliseconds can mean millions of dollars of more revenue.

Describe the solution you'd like

Add field to svelte.config.cjs to automatically prefetch pages. Perhaps something like this?

module.exports = {
  kit: {
    prefetchLinks: true,
  },
};

Now there are likely valid reasons to not prefetch certain links, in which case you also need a way to opt-out of prefetching. This example seems like intuitive syntax:

<a sveltekit:prefetch={false} href="blog/what-is-sveltekit">What is SvelteKit?</a>

Describe alternatives you've considered

As far as I know, the current best solution is to add sveltekit:prefetch to every single <a> tag.

Alternatively, you could call prefetchRoutes(routes) on every page, but this is wasteful (all routes will be prefetched) and may not work with dynamic content.

How important is this feature to you?

I'll keep using Svelte/kit if this doesn't get added, but I'd be ecstatic if it gets added!

@dummdidumm dummdidumm added feature / enhancement New feature or request router labels Mar 31, 2021
@swyxio
Copy link
Contributor

swyxio commented Mar 31, 2021

i do like this! i'd change the name to prefetchLinks instead of prefetch to make it less ambiguous and leave room for other types of prefetch.

perhaps offer the option to opt out by page level as well - in the <script type="module"> section

@Nick-Mazuk
Copy link
Contributor Author

i do like this! i'd change the name to prefetchLinks instead of prefetch to make it less ambiguous and leave room for other types of prefetch.

Great idea! Updated original post with that edit.

perhaps offer the option to opt out by page level as well - in the <script type="module"> section

That would make sense. We could potentially use the same syntax for prerendering to keep things consistent.

<script context="module">
	export const prefetchLinks = false;
</script>

@dummdidumm
Copy link
Member

Not sure about the context="module" proposal, since one could also override it at the <a> tag level, which is not the case for the other options.

@madeleineostoja
Copy link

I think this is a great idea, moreso for content you don't have control over (eg: links in content from a CMS) than for simply saving keystrokes. It's also the default of NextJS, which is an industry leader for a reason.

I'd second that the context="module" config option isn't needed, since you can override it on a link by link basis.

@vwkd
Copy link

vwkd commented Aug 5, 2021

I'm all for this. I'd go even further and propose to make prefetching links by default the default such that there is an option to opt-out instead of opt-in. SvelteKit should be fast by default, not make the user search around for options to tweak to make it even faster. Instead of when to opt-in, the manual should simply clarify when to opt-out.

@hitrop
Copy link

hitrop commented Sep 5, 2021

Also, there should be someway for the prefetched links to not call the load method. The load call should be deferred untill the user actually clicks the link. For example, if you are building a football match app, then just hovering over the match link will prefetch the js bundle including the current score by calling load. So, if the user clicks the match link after 1 hour, he will be seeing the score of 1 hour before (which is bad). Ideally we want to call load only after the user actually clicks it (I think NextJS does it this way).

So, there should be following options on the achor tag:

prefetch_hover_no_load
prefetch_hover_load

And also, some options to make prefetching of links in viewport (Like in NextJS)
prefetch_viewport_no_load.
prefetch_viewport_load.

@madeleineostoja
Copy link

Is there any movement on this / input from maintainers? I think it's a bit of a no-brainer feature and after coming back to Sveltekit after using React/Next for work for the last 6 months it jumped out as a rough edge. I'd strongly second @vwkd in that Sveltekit should be as performant as possible by default, rather than requiring this flag on every anchor in the app, especially those which cannot be controlled by the user.

@pierredewilde
Copy link

Prefetching is currently implemented by hovering on <a> links.

Therefore, it is a desktop only feature.

How can we prefetch on mobile ?

@bluwy
Copy link
Member

bluwy commented Feb 11, 2022

How can we prefetch on mobile ?

For mobile, SvelteKit listens to the touchstart event, which usually happens before the actual anchor navigation, so between that extra timeframe is where prefetching happens.

@Rich-Harris Rich-Harris added this to the 1.0 milestone Apr 7, 2022
@benmccann benmccann added the p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. label Jul 29, 2022
@Rich-Harris
Copy link
Member

@Conduitry came up with a brilliantly simple solution to this problem the other day:

<nav>
  <svg viewBox="0 0 2 3" aria-hidden="true">
    <path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
  </svg>
-  <ul>
+  <ul data-sveltekit-prefetch>
    <li class:active={$page.url.pathname === '/'}>
-      <a data-sveltekit-prefetch href="/">Home</a>
+      <a href="/">Home</a>
    </li>
    <li class:active={$page.url.pathname === '/about'}>
-      <a data-sveltekit-prefetch href="/about">About</a>
+      <a href="/about">About</a>
    </li>
    <li class:active={$page.url.pathname === '/todos'}>
-      <a data-sveltekit-prefetch href="/todos">Todos</a>
+      <a href="/todos">Todos</a>
    </li>
  </ul>
  <svg viewBox="0 0 2 3" aria-hidden="true">
    <path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
  </svg>
</nav>

(Note that sveltekit:prefetch is changing to data-sveltekit-prefetch — #6170.)

In other words: instead of just checking the element itself, we check its parents until we find a data-sveltekit-prefetch attribute. It's therefore easy to opt your whole <body> into this behaviour.

In that case we'd need a way to opt out for a subtree:

<body data-sveltekit-prefetch>
  <!-- these are all prefetched -->
  <a href="/a">...</a>
  <a href="/b">...</a>
  <a href="/c">...</a>

  <!-- but these are not -->
  <div data-sveltekit-prefetch="off">
    <a href="/x">...</a>
    <a href="/y">...</a>
    <a href="/z">...</a>
  </div>
</div>

(In future we could add other modes besides 'prefetch when a mouse/finger stops over a link', like eager, which could mean 'prefetch as soon as this link is created following navigation'.)

For consistency, it would be nice to do the same thing for data-sveltekit-reload and data-sveltekit-noscroll:

<div data-sveltekit-reload>
  <!-- all links inside here will trigger a full page reload... -->

  <div data-sveltekit-reload="off">
    <!-- ...except these ones -->
  </div>
</div>

<div data-sveltekit-noscroll="off">
  <!-- all links inside here will preserve the current scroll position... -->

  <div data-sveltekit-noscroll>
    <!-- ...except these ones -->
  </div>
</div>

@madeleineostoja
Copy link

madeleineostoja commented Aug 30, 2022

Would this mean we could just add the prop to a root layout <slot /> to apply it by default to the whole app? Also a big fan of the eager proposal in future for flagging preloads declaratively (eg: next step in a user flow).

And FWIW it's still really unfortunate that the sveltekit: props are changing to data-sveltekit- unless they absolutely have to. The existing properties map so intuitively to the syntax already established by the rest of svelte, at the cost of technical validity

@Rich-Harris
Copy link
Member

Applying it to <slot /> would have no effect, because it's not a 'real' element — it doesn't take attributes. But you could apply it to the <body> in your src/app.html or to a top-level element like <main> in your root layout.

And yeah, I know :( The hope is that with the other change we'll only have to look at it rarely

@madeleineostoja
Copy link

madeleineostoja commented Aug 30, 2022

Oh yeah derp, but sweet! Sounds like a good solution, and in line with moving more stuff from config to code (ssr, prerender, hydrate, etc).

I do think sveltekit should be as performant as possible by default, which means defaulting to prefetching like Next does, but I guess under this scheme that can be 'solved' with documentation and adding the attribute to body in the starter example/template. So it becomes a non-issue, the real 'issue' now is that sveltekit requires significant mucking about (attributes on every anchor) for the performant path

@jerrygreen
Copy link

jerrygreen commented Aug 30, 2022

Does that mean that data-sveltekit-prefetch is already available in the most recent svelte & sveltekit?

I mean, this issue still not closed… Not available in versions published on npm yet?

UPD: seems to be available in @sveltejs/kit@1.0.0-next.454, not sure why issue still not closed

@madeleineostoja
Copy link

@jerrygreen this issue is for having data-sveltekit-prefetch on root nodes that children inherit from, which isn't yet implemented

Rich-Harris added a commit that referenced this issue Aug 31, 2022
* failing tests for #790

* implement parent data-sveltekit-foo attributes

* changeset

* update docs

* move data-sveltekit-prefetch to <nav> in demo app

* update docs

* fix link

* whoops, meant to uncomment

* fix test

* remove unused function

* Update packages/kit/src/runtime/client/client.js

Co-authored-by: Conduitry <git@chor.date>

* Update documentation/docs/09-link-options.md

Co-authored-by: Conduitry <git@chor.date>

* Update .changeset/fresh-lemons-fail.md

Co-authored-by: Conduitry <git@chor.date>

Co-authored-by: Conduitry <git@chor.date>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. prefetch router
Projects
None yet
Development

Successfully merging a pull request may close this issue.