-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* shallow routing * add types * drive-by fix - bad merge * warn on use of history.pushState and history.replaceState * regenerate types * make url the first argument, even though it's optional — this is more future-proof, as we may add options in future * tests * remove state from goto * tidy up * tidy up internal navigate API a bit * more * use current.url for invalidation, not location.href * add docs * copy-paste fail * on second thoughts * links * link * regenerate types * update preloadData docs * fix preloadData docs * drive-by fix * Apply suggestions from code review Co-authored-by: Ignatius Bagus <[email protected]> * tweaks * changeset, breaking change docs * code-golf * this seems unnecessary * mention preloadData * use original replace state * oops * handle SPA case * more involved example - show importing a +page.svelte and correctly handling a click event, and avoid --------- Co-authored-by: Rich Harris <[email protected]> Co-authored-by: Ben McCann <[email protected]> Co-authored-by: Simon H <[email protected]> Co-authored-by: Ignatius Bagus <[email protected]> Co-authored-by: Simon Holthausen <[email protected]>
- Loading branch information
1 parent
a00183a
commit 052adf9
Showing
30 changed files
with
673 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@sveltejs/kit": minor | ||
--- | ||
|
||
feat: implement shallow routing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@sveltejs/kit": major | ||
--- | ||
|
||
breaking: remove state option from goto in favor of shallow routing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
--- | ||
title: Shallow routing | ||
--- | ||
|
||
As you navigate around a SvelteKit app, you create _history entries_. Clicking the back and forward buttons traverses through this list of entries, re-running any `load` functions and replacing page components as necessary. | ||
|
||
Sometimes, it's useful to create history entries _without_ navigating. For example, you might want to show a modal dialog that the user can dismiss by navigating back. This is particularly valuable on mobile devices, where swipe gestures are often more natural than interacting directly with the UI. In these cases, a modal that is _not_ associated with a history entry can be a source of frustration, as a user may swipe backwards in an attempt to dismiss it and find themselves on the wrong page. | ||
|
||
SvelteKit makes this possible with the [`pushState`](/docs/modules#$app-navigation-pushstate) and [`replaceState`](/docs/modules#$app-navigation-replacestate) functions, which allow you to associate state with a history entry without navigating. For example, to implement a history-driven modal: | ||
|
||
```svelte | ||
<!--- file: +page.svelte ---> | ||
<script> | ||
import { pushState } from '$app/navigation'; | ||
import { page } from '$app/stores'; | ||
import Modal from './Modal.svelte'; | ||
function showModal() { | ||
pushState('', { | ||
showModal: true | ||
}); | ||
} | ||
</script> | ||
{#if $page.state.showModal} | ||
<Modal close={() => history.back()} /> | ||
{/if} | ||
``` | ||
|
||
The modal can be dismissed by navigating back (unsetting `$page.state.showModal`) or by interacting with it in a way that causes the `close` callback to run, which will navigate back programmatically. | ||
|
||
## API | ||
|
||
The first argument to `pushState` is the URL, relative to the current URL. To stay on the current URL, use `''`. | ||
|
||
The second argument is the new page state, which can be accessed via the [page store](/docs/modules#$app-stores-page) as `$page.state`. You can make page state type-safe by declaring an [`App.PageState`](/docs/types#app) interface (usually in `src/app.d.ts`). | ||
|
||
To set page state without creating a new history entry, use `replaceState` instead of `pushState`. | ||
|
||
## Loading data for a route | ||
|
||
When shallow routing, you may want to render another `+page.svelte` inside the current page. For example, clicking on a photo thumbnail could pop up the detail view without navigating to the photo page. | ||
|
||
For this to work, you need to load the data that the `+page.svelte` expects. A convenient way to do this is to use [`preloadData`](/docs/modules#$app-navigation-preloaddata) inside the `click` handler of an `<a>` element. If the element (or a parent) uses [`data-sveltekit-preload-data`](/docs/link-options#data-sveltekit-preload-data), the data will have already been requested, and `preloadData` will reuse that request. | ||
|
||
```svelte | ||
<!--- file: src/routes/photos/+page.svelte ---> | ||
<script> | ||
import { preloadData, pushState, goto } from '$app/navigation'; | ||
import Modal from './Modal.svelte'; | ||
import PhotoPage from './[id]/+page.svelte'; | ||
export let data; | ||
</script> | ||
{#each data.thumbnails as thumbnail} | ||
<a | ||
href="/photos/{thumbnail.id}" | ||
on:click={async (e) => { | ||
// bail if opening a new tab, or we're on too small a screen | ||
if (e.metaKey || innerWidth < 640) return; | ||
// prevent navigation | ||
e.preventDefault(); | ||
const { href } = e.currentTarget; | ||
// run `load` functions (or rather, get the result of the `load` functions | ||
// that are already running because of `data-sveltekit-preload-data`) | ||
const result = await preloadData(href); | ||
if (result.type === 'loaded' && result.status === 200) { | ||
pushState(href, { selected: result.data }); | ||
} else { | ||
// something bad happened! try navigating | ||
goto(href); | ||
} | ||
}} | ||
> | ||
<img alt={thumbnail.alt} src={thumbnail.src} /> | ||
</a> | ||
{/each} | ||
{#if $page.state.selected} | ||
<Modal on:close={() => history.goBack()}> | ||
<!-- pass page data to the +page.svelte component, | ||
just like SvelteKit would on navigation --> | ||
<PhotoPage data={$page.state.selected} /> | ||
</Modal> | ||
{/if} | ||
``` | ||
|
||
## Caveats | ||
|
||
During server-side rendering, `$page.state` is always an empty object. The same is true for the first page the user lands on — if the user reloads the page, state will _not_ be applied until they navigate. | ||
|
||
Shallow routing is a feature that requires JavaScript to work. Be mindful when using it and try to think of sensible fallback behavior in case JavaScript isn't available. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.