Skip to content

Commit

Permalink
docs: sync repos (#677)
Browse files Browse the repository at this point in the history
* docs: sync repos

* fix links
  • Loading branch information
dummdidumm authored Oct 28, 2024
1 parent 05c4dcf commit 1d2e52d
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 89 deletions.
45 changes: 29 additions & 16 deletions apps/svelte.dev/content/docs/kit/20-core-concepts/10-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,22 @@ A `+page.svelte` component defines a page of your app. By default, pages are ren
<a href="/">Home</a>
```

Pages can receive data from `load` functions via the `data` prop.

```svelte
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.title}</h1>
<div>{@html data.content}</div>
```

> [!LEGACY] In Svelte 4
> In Svelte 4, you'd use `export let data` instead
> [!NOTE] Note that SvelteKit uses `<a>` elements to navigate between routes, rather than a framework-specific `<Link>` component.
### +page.js
Expand Down Expand Up @@ -153,21 +158,29 @@ But in many apps, there are elements that should be visible on _every_ page, suc

To create a layout that applies to every page, make a file called `src/routes/+layout.svelte`. The default layout (the one that SvelteKit uses if you don't bring your own) looks like this...

```html
<slot></slot>
```svelte
<script>
let { children } = $props();
</script>
{@render children()}
```

...but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a `<slot>` for the page content. For example, let's add a nav bar:
...but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a `@render` tag for the page content. For example, let's add a nav bar:

```svelte
<!---- file: src/routes/+layout.svelte --->
<script>
let { children } = $props();
</script>
```html
/// file: src/routes/+layout.svelte
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/settings">Settings</a>
</nav>
<slot></slot>
{@render children()}
```

If we create pages for `/`, `/about` and `/settings`...
Expand Down Expand Up @@ -196,8 +209,8 @@ We can create a layout that only applies to pages below `/settings` (while inher
```svelte
<!--- file: src/routes/settings/+layout.svelte --->
<script>
/** @type {import('./$types').LayoutData} */
export let data;
/** @type {{ data: import('./$types').LayoutData, children: import('svelte').Snippet }} */
let { data, children } = $props();
</script>
<h1>Settings</h1>
Expand All @@ -208,7 +221,7 @@ We can create a layout that only applies to pages below `/settings` (while inher
{/each}
</div>
<slot></slot>
{@render children()}
```

You can see how `data` is populated by looking at the `+layout.js` example in the next section just below.
Expand Down Expand Up @@ -239,8 +252,8 @@ Data returned from a layout's `load` function is also available to all its child
```svelte
<!--- file: src/routes/settings/profile/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
Expand Down Expand Up @@ -370,13 +383,13 @@ export async function fallback({ request }) {
Throughout the examples above, we've been importing types from a `$types.d.ts` file. This is a file SvelteKit creates for you in a hidden directory if you're using TypeScript (or JavaScript with JSDoc type annotations) to give you type safety when working with your root files.
For example, annotating `export let data` with `PageData` (or `LayoutData`, for a `+layout.svelte` file) tells TypeScript that the type of `data` is whatever was returned from `load`:
For example, annotating `let { data } = $props()` with `PageData` (or `LayoutData`, for a `+layout.svelte` file) tells TypeScript that the type of `data` is whatever was returned from `load`:
```svelte
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
```
Expand Down
35 changes: 19 additions & 16 deletions apps/svelte.dev/content/docs/kit/20-core-concepts/20-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ export function load({ params }) {
```svelte
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
```

> [!LEGACY] In Svelte 4
> In Svelte 4, you'd use `export let data` instead
Thanks to the generated `$types` module, we get full type safety.

A `load` function in a `+page.js` file runs both on the server and in the browser (unless combined with `export const ssr = false`, in which case it will [only run in the browser](page-options#ssr)). If your `load` function should _always_ run on the server (because it uses private environment variables, for example, or accesses a database) then it would go in a `+page.server.js` instead.
Expand Down Expand Up @@ -85,13 +88,13 @@ export async function load() {
```svelte
<!--- file: src/routes/blog/[slug]/+layout.svelte --->
<script>
/** @type {import('./$types').LayoutData} */
export let data;
/** @type {{ data: import('./$types').LayoutData, children: Snippet }} */
let { data, children } = $props();
</script>
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
<!-- +page.svelte is `@render`ed here -->
{@render children()}
</main>
<aside>
Expand All @@ -115,13 +118,13 @@ Data returned from layout `load` functions is available to child `+layout.svelte
<script>
+++import { page } from '$app/stores';+++
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
+++ // we can access `data.posts` because it's returned from
// the parent layout `load` function
let index = $derived(data.posts.findIndex(post => post.slug === $page.params.slug));
let next = $derived(data.posts[index - 1];)+++
let next = $derived(data.posts[index + 1]);+++
</script>
<h1>{data.post.title}</h1>
Expand Down Expand Up @@ -330,7 +333,7 @@ export async function load({ fetch, setHeaders }) {
}
```

Setting the same header multiple times (even in separate `load` functions) is an error — you can only set a given header once. You cannot add a `set-cookie` header with `setHeaders` — use `cookies.set(name, value, options)` instead.
Setting the same header multiple times (even in separate `load` functions) is an error. You can only set a given header once using the `setHeaders` function. You cannot add a `set-cookie` header with `setHeaders` — use `cookies.set(name, value, options)` instead.

## Using parent data

Expand Down Expand Up @@ -365,8 +368,8 @@ export async function load({ parent }) {
```svelte
<!--- file: src/routes/abc/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<!-- renders `1 + 2 = 3` -->
Expand Down Expand Up @@ -504,8 +507,8 @@ This is useful for creating skeleton loading states, for example:
```svelte
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
</script>
<h1>{data.post.title}</h1>
Expand Down Expand Up @@ -645,8 +648,8 @@ export async function load({ fetch, depends }) {
<script>
import { invalidate, invalidateAll } from '$app/navigation';
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,8 @@ export const actions = {
```svelte
<!--- file: src/routes/login/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;

/** @type {import('./$types').ActionData} */
export let form;
/** @type {{ data: import('./$types').PageData, form: import('./$types').ActionData }} */
let { data, form } = $props();
</script>

{#if form?.success}
Expand All @@ -154,6 +151,9 @@ export const actions = {
{/if}
```
> [!LEGACY] In Svelte 4
> In Svelte 4, you'd use `export let data` and `export let form` instead to declare properties
### Validation errors
If the request couldn't be processed because of invalid data, you can return validation errors — along with the previously submitted form values — back to the user so that they can try again. The `fail` function lets you return an HTTP status code (typically 400 or 422, in the case of validation errors) along with the data. The status code is available through `$page.status` and the data through `form`:
Expand Down Expand Up @@ -339,8 +339,8 @@ The easiest way to progressively enhance a form is to add the `use:enhance` acti
<script>
+++import { enhance } from '$app/forms';+++

/** @type {import('./$types').ActionData} */
export let form;
/** @type {{ form: import('./$types').ActionData }} */
let { form } = $props();
</script>

<form method="POST" +++use:enhance+++>
Expand Down Expand Up @@ -390,8 +390,8 @@ If you return a callback, you may need to reproduce part of the default `use:enh
<script>
import { enhance, +++applyAction+++ } from '$app/forms';

/** @type {import('./$types').ActionData} */
export let form;
/** @type {{ form: import('./$types').ActionData }} */
let { form } = $props();
</script>

<form
Expand Down Expand Up @@ -427,11 +427,8 @@ We can also implement progressive enhancement ourselves, without `use:enhance`,
import { invalidateAll, goto } from '$app/navigation';
import { applyAction, deserialize } from '$app/forms';

/** @type {import('./$types').ActionData} */
export let form;

/** @type {any} */
let error;
/** @type {{ form: import('./$types').ActionData }} */
let { form } = $props();

/** @param {{ currentTarget: EventTarget & HTMLFormElement}} event */
async function handleSubmit(event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ You might wonder how we're able to use `$page.data` and other [app stores]($app-
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
/** @type {import('./$types').LayoutData} */
export let data;
/** @type {{ data: import('./$types').LayoutData }} */
let { data } = $props();
// Create a store and update it when necessary...
const user = writable();
$: user.set(data.user);
const user = writable(data.user);
$effect.pre(() => {
user.set(data.user);
});
// ...and add it to the context for child components to access
setContext('user', user);
Expand Down Expand Up @@ -125,8 +127,8 @@ When you navigate around your application, SvelteKit reuses existing layout and
```svelte
<!--- file: src/routes/blog/[slug]/+page.svelte --->
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
// THIS CODE IS BUGGY!
const wordCount = data.content.split(' ').length;
Expand All @@ -148,8 +150,8 @@ Instead, we need to make the value [_reactive_](/tutorial/svelte/state):
```svelte
/// file: src/routes/blog/[slug]/+page.svelte
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {{ data: import('./$types').PageData }} */
let { data } = $props();
+++ let wordCount = $state(data.content.split(' ').length);
let estimatedReadingTime = $derived(wordCount / 250);+++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ export function load() {
```svelte
<!--- file: +layout.svelte --->
<script>
/** @type {import('./$types').LayoutServerData} */
export let data;
/** @type {{ data: import('./$types').LayoutServerData }} */
let { data } = $props();
</script>
<p>This staging environment was deployed from {data.deploymentGitBranch}.</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ You can also put a `+page` directly inside a `(group)`, for example if `/` shoul

### Breaking out of layouts

The root layout applies to every page of your app — if omitted, it defaults to `<slot />`. If you want some pages to have a different layout hierarchy than the rest, then you can put your entire app inside one or more groups _except_ the routes that should not inherit the common layouts.
The root layout applies to every page of your app — if omitted, it defaults to `{@render children()}`. If you want some pages to have a different layout hierarchy than the rest, then you can put your entire app inside one or more groups _except_ the routes that should not inherit the common layouts.

In the example above, the `/admin` route does not inherit either the `(app)` or `(marketing)` layouts.

Expand Down Expand Up @@ -260,11 +260,11 @@ Not all use cases are suited for layout grouping, nor should you feel compelled
<!--- file: src/routes/nested/route/+layout@.svelte --->
<script>
import ReusableLayout from '$lib/ReusableLayout.svelte';
export let data;
let { data, children } = $props();
</script>
<ReusableLayout {data}>
<slot />
{@render children()}
</ReusableLayout>
```

Expand Down
6 changes: 5 additions & 1 deletion apps/svelte.dev/content/docs/kit/30-advanced/20-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export async function handle({ event, resolve }) {

> [!NOTE] Requests for static assets — which includes pages that were already prerendered — are _not_ handled by SvelteKit.
If unimplemented, defaults to `({ event, resolve }) => resolve(event)`. To add custom data to the request, which is passed to handlers in `+server.js` and server `load` functions, populate the `event.locals` object, as shown below.
If unimplemented, defaults to `({ event, resolve }) => resolve(event)`.

### locals

To add custom data to the request, which is passed to handlers in `+server.js` and server `load` functions, populate the `event.locals` object, as shown below.

```js
/// file: src/hooks.server.js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ To do this, export a `snapshot` object with `capture` and `restore` methods from
```svelte
<!--- file: +page.svelte --->
<script>
let comment = '';
let comment = $state('');
/** @type {import('./$types').Snapshot<string>} */
export const snapshot = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ For this to work, you need to load the data that the `+page.svelte` expects. A c
import Modal from './Modal.svelte';
import PhotoPage from './[id]/+page.svelte';
export let data;
let { data } = $props();
</script>
{#each data.thumbnails as thumbnail}
Expand Down
23 changes: 23 additions & 0 deletions apps/svelte.dev/content/docs/kit/40-best-practices/03-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Auth
---

Auth refers to authentication and authorization, which are common needs when building a web application. Authentication means verifying that the user is who they say they are based on their provided credentials. Authorization means determining which actions they are allowed to take.

## Sessions vs tokens

After the user has provided their credentials such as a username and password, we want to allow them to use the application without needing to provide their credentials again for future requests. Users are commonly authenticated on subsequent requests with either a session identifier or signed token such as a JSON Web Token (JWT).

Session IDs are most commonly stored in a database. They can be immediately revoked, but require a database query to be made on each request.

In contrast, JWT generally are not checked against a datastore, which means they cannot be immediately revoked. The advantage of this method is improved latency and reduced load on your datastore.

## Integration points

Auth [cookies](@sveltejs-kit#Cookies) can be checked inside [server hooks](hooks#Server-hooks). If a user is found matching the provided credentials, the user information can be stored in [`locals`](hooks#Server-hooks-handle).

## Guides

[Lucia](https://lucia-next.pages.dev/) is a reference for session-based web app auth. It contains example code snippets and projects for implementing session-based auth within SvelteKit and other JS projects. You can add code which follows the Lucia guide to your project with `npx sv create` when creating a new project or `npx sv add lucia` for an existing project.

An auth system is tightly coupled to a web framework because most of the code lies in validating user input, handling errors, and directing users to the appropriate next page. As a result, many of the generic JS auth libraries include one or more web frameworks within them. For this reason, many users will find it preferrable to follow a SvelteKit-specific guide such as the examples found in [Lucia](https://lucia-next.pages.dev/) rather than having multiple web frameworks inside their project.
Loading

0 comments on commit 1d2e52d

Please sign in to comment.