Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/routes/guides/routing-and-navigation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,7 @@ The preload function is then passed in the `<Route>` definition:
You can export preload functions and data wrappers that correspond to routes from a dedicated `[route].data.js` or `[route].data.ts` file.
This pattern provides a way to import the data function without loading anything else.

```jsx
// src/pages/users/[id].data.js
```tsx title="src/pages/users/[id].data.js"
import { query } from "@solidjs/router";

export const getUser = query(async (id) => {
Expand Down Expand Up @@ -494,8 +493,7 @@ render(
`[id].jsx` contains the component that gets rendered.
When you wrap the function within [`createAsync`](/solid-router/reference/data-apis/create-async) with the imported function, it will yield [a signal](/routes/concepts/signals) once the anticipated promise resolves.

```jsx
// [id].jsx
```tsx title="[id].tsx"
import { createAsync } from "@solidjs/router";
import { getUser } from "./[id].data";

Expand Down
73 changes: 62 additions & 11 deletions src/routes/reference/component-apis/lazy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,75 @@
title: lazy
---

```ts
Used to lazy load components to allow for code splitting.
Components are not loaded until rendered or manually preloaded.

```tsx title="app.tsx"
import { lazy } from "solid-js"

const ComponentA = lazy(() => import("./ComponentA"));

function App(props: { title: string }) {
return (
<ComponentA title={props.title} />
)
}
```

Lazy loaded components can be used the same as its statically imported counterpart, receiving props etc.
Lazy components trigger `<Suspense>`

## Preloading data in Nested Lazy Components

Top-level lazy components will automatically be preloaded as well as their preload functions.
However, nested lazy components will not be preloaded automatically because they are not part of the route hierarchy.
To preload such components, you can use the `preload` method exposed on the lazy component.

```tsx title="component-with-preload.tsx"
import { lazy } from "solid-js"
import type { Component } from "solid-js"

const Nested = lazy(() => import("./Nested"))

const ComponentWithPreload: Component = () => {
// preload Nested component when needed
async function handlePreload() {
await Nested.preload()
}

return (
<div>
<button onClick={handlePreload}>Preload Nested Component</button>
Copy link

Choose a reason for hiding this comment

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

By the time the button is visible and before it's able to be clicked, <Nested /> is loaded and rendered already no?

<Nested />
</div>
)
}

```

## Type Signature

```tsx
function lazy<T extends Component<any>>(
fn: () => Promise<{ default: T }>
): T & { preload: () => Promise<T> }
```

Used to lazy load components to allow for code splitting.
Components are not loaded until rendered.
Lazy loaded components can be used the same as its statically imported counterpart, receiving props etc.
Lazy components trigger `<Suspense>`
### Type Parameters

```tsx
// wrap import
const ComponentA = lazy(() => import("./ComponentA"));
| Name | Constraint | Description |
| ---- | ---------- | ----------- |
| `T` | `Component<any>` | The component type that will be lazily loaded (including its props).

### Parameters

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `fn` | `() => Promise<{ default: T }>` | Yes | A function that returns a dynamic import resolving to the component as the `default` export. |

### Returns

| Type | Description |
| ---- | ----------- |
| `T & { preload: () => Promise<T> }` | A renderable component compatible with `T` that also exposes a `preload()` method to eagerly load the module. |

// use in JSX
<ComponentA title={props.title} />
```
2 changes: 1 addition & 1 deletion src/routes/solid-router/advanced-concepts/data.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"title": "Advanced concepts",
"pages": ["lazy-loading.mdx"]
"pages": ["preloading.mdx", "lazy-loading.mdx"]
}
24 changes: 24 additions & 0 deletions src/routes/solid-router/advanced-concepts/preloading.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: Preloading
---

Anchors in Solid Router will preload routes by default on link hover/focus to improve perceived performance.

To enhance preloading, you can define the `preload` function on your route definition.
When on a [SolidStart](/solid-start) application, this function can also run on the server during the initial page load to start fetching data before rendering. When in a Single-Page Application (SPA), it will load the route's component and its `preload` function when the user hovers or focuses on a link.

| user action | route behavior |
| ----------- | -------------------------------------- |
| hover | with a 300ms delay to avoid excessive preloading |
| focus | immediately |
Comment on lines +8 to +13
Copy link
Member

Choose a reason for hiding this comment

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

I think the SolidStart section might be more appropriate in the reference section as a callout so it doesn't get buried here.

Following that change, I also believe it might be better to show a basic code snippet of what the hover would look like. Doesn't have to be too complex, just enough to get the idea across for how it should look.

Copy link
Member Author

Choose a reason for hiding this comment

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

the code snippet is in the respective API references. This entry is to explain conceptually how preloading works in Solid Router. I added links to the the APIs so things didn't get too repetitive

Not sure what you mean about the hover snippet, that's just the browser API, there's no code from the user to manage or handle that. If they hover or focus the anchor tag, preloading will happen.

Remember: preloading is default behavior. Altering it is an advanced concept for users, most cases we want people to feel it "just works".

Copy link
Member

Choose a reason for hiding this comment

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

I think as it stands for me it feels more like an extension of the ref page vs an exposure to the concept as a whole. What I meant by the hover example was that it might make it feel less like a ref page if there is some form of an example on the page of what it'd look like to modify it.

My explanation wasn't entirely great (I'm sorry about that) but I'll try to give you a bit more of an idea as to what I mean tomorrow (unless @devagrawal09 has any ideas before then).

Copy link
Member Author

Choose a reason for hiding this comment

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

if there is some form of an example on the page of what it'd look like to modify it.
There's no way to modify the preload threshold. This is a magical number to prevent eager preloading.

The hover in this case is the regular browser hover, there's nothing framework specific on this. Regular mouse hovering a link will fire a 300ms timeout that will preload data once fulfilled.

Extending on this right here feels to me like documenting browser APIs or framework implementation details.
The reason I added this table here is only to be more transparent about how the framework works under the hood.


## Imperative Preloading

You can also use the [`usePreloadRoute`](/solid-router/reference/primitives/use-preload-route) helper to preload routes programmatically in response to events other than link hover/focus, such as button clicks or timers.
This helper will load only the route's component by default, but it can receive a configuration object to also load the data.

## Preloading and Lazy Loading

When a route has nested lazy components, such components will not be part of the route hierarchy, so they **will not** be preloaded with the route. To preload such components, you can use the [`usePreloadRoute`](/solid-router/reference/primitives/use-preload-route) helper in the parent component to load them when needed.
Copy link

Choose a reason for hiding this comment

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

Given the context of this paragraph, the second sentence here seems misplaced and was the problem we were initially discussing on Discord. usePreloadRoute is for preloading routes directly. (await lazy(string)).preload() is what's necessary to properly preload nested lazy components. Was this maybe an editing mixup?

Copy link
Member Author

Choose a reason for hiding this comment

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

100%
Great catch!
I need to reword this entire paragraph 🤦

Copy link

Choose a reason for hiding this comment

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

Cool, you seem pretty busy, so I can give it a try. Hopefully it's in the ballpark of what you had in mind.

Copy link

Choose a reason for hiding this comment

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

Looks like the docs for the lazy API could use a mention of its preload feature more explicitly instead of just in its types. https://docs.solidjs.com/reference/component-apis/lazy

Suggested change
When a route has nested lazy components, such components will not be part of the route hierarchy, so they **will not** be preloaded with the route. To preload such components, you can use the [`usePreloadRoute`](/solid-router/reference/primitives/use-preload-route) helper in the parent component to load them when needed.
When a route has nested lazy components, such components will not be part of the route hierarchy, so they **will not** be preloaded with the route. To preload such components, you can use the `preload()` function returned from calling the [`lazy()`](https://docs.solidjs.com/reference/component-apis/lazy) component API.

To learn more about lazy loading components, see the [`lazy`](/reference/component-apis/lazy#preloading-data-in-nested-lazy-components) documentation.
24 changes: 22 additions & 2 deletions src/routes/solid-router/reference/primitives/use-preload-route.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@
title: usePreloadRoute
---

`usePreloadRoute` returns a function that can be used to preload a route manually. This is what happens automatically with link hovering and similar focus based behavior, but it is available here as an API.
`usePreloadRoute` returns a function that can be used to preload a route manually.

```js
```ts
const preload = usePreloadRoute();

preload(`/users/settings`, { preloadData: true });
```

## Usage

Routes are preloaded by default within Solid Router contexts.
This helper is useful when you want to preload a route in response to some other event, such as a button click or a timer.

## Type Signature

### Parameters

| Parameter | Type | Required | Description |
| --------- | -------- | -------- | ------------------------------------ |
| `to` | `To` | Yes | The route path to preload |
| `options` | `object` | No | Configuration options for preloading |

### Options

| Option | Type | Default | Description |
| ------------- | --------- | ------- | ------------------------------------------------------------------- |
| `preloadData` | `boolean` | `false` | Whether to preload the route's data in addition to the route itself |