Data loading
What's a modern app without some data to power it. SolidStart aims to make it easy to load data from from data sources. It will help you keep your UI always uptote date with your data.
For most your data requirements, you will likely be using the route as the indicator of what data to load. This is because the route is the primary way of navigating your app. SolidStart start already has nested routing to help structure your apps in a heirarchical way. Each route comes with the ability to export its own routeData
function that will be managed my the router. Yeah, the router is also the data manager.
Solid has a createResource
primitive that takes an async function and returns a signal from it. Its a great starting place for your data needs. It integrates with Suspense and ErrorBoundary to help you manage your lifecycle.
Lets take a look at how we can use this to load data from a third party API for our app.
tsx
import { createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js";
export function routeDatafunction routeData(): {
students: Resource<any>;
}
() { const [studentsconst students: Resource<any>
] = createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) createResource<any, unknown>(fetcher: ResourceFetcher<true, any, unknown>, options?: ResourceOptions<any, true> | undefined): ResourceReturn<any, unknown> (+3 overloads)
import createResource
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
(); });
return { students(property) students: Resource<any>
}; }
tsx
import { createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js";
export function routeDatafunction routeData(): {
students: Resource<any>;
}
() { const [studentsconst students: Resource<any>
] = createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) createResource<any, unknown>(fetcher: ResourceFetcher<true, any, unknown>, options?: ResourceOptions<any, true> | undefined): ResourceReturn<any, unknown> (+3 overloads)
import createResource
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
(); });
return { students(property) students: Resource<any>
}; }
Now your component can use the useRouteData
function to access the data that is returned by routeData
.
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
} from "solid-start";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): {
students: Resource<Student[]>;
}
() { const [studentsconst students: Resource<Student[]>
] = createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) createResource<Student[], unknown>(fetcher: ResourceFetcher<true, Student[], unknown>, options?: ResourceOptions<Student[], true> | undefined): ResourceReturn<...> (+3 overloads)
import createResource
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
return { students(property) students: Resource<Student[]>
}; }
export function Pagefunction Page(): JSX.Element
() { const { studentsconst students: Resource<Student[]>
} = useRouteData(alias) useRouteData<() => {
students: Resource<Student[]>;
}>(): {
students: Resource<Student[]>;
}
import useRouteData
<typeof routeDatafunction routeData(): {
students: Resource<Student[]>;
}
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
} from "solid-start";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): {
students: Resource<Student[]>;
}
() { const [studentsconst students: Resource<Student[]>
] = createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) createResource<Student[], unknown>(fetcher: ResourceFetcher<true, Student[], unknown>, options?: ResourceOptions<Student[], true> | undefined): ResourceReturn<...> (+3 overloads)
import createResource
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
return { students(property) students: Resource<Student[]>
}; }
export function Pagefunction Page(): JSX.Element
() { const { studentsconst students: Resource<Student[]>
} = useRouteData(alias) useRouteData<() => {
students: Resource<Student[]>;
}>(): {
students: Resource<Student[]>;
}
import useRouteData
<typeof routeDatafunction routeData(): {
students: Resource<Student[]>;
}
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
Caveats:
- The
routeData
function is only called once per route. This is because the router is managing the lifecycle of the data. If you need to refresh the data, you can use the refresh
function that is returned by createResource
. Or other ways to refetch your resources.
- The
routeData
function is called before the route is rendered. It doesn't share the same context
as the route. The context tree that is exposed to the routeData
function is anything above the Routes
component.
- The component receives exactly what the
routeData
function returns when they call useRouteData
. This means that you can return anything you want from the routeData
function.
- The
routeData
function is called on the server and the client. The resources declared in the routeData
function can hydrate using serialized data from the server-side render.
routeData
functions are run on the client when you first navigate to a route
and never again.
- The server-side render will only wait for the resources to fetch if the resource signals are accessed under a
Suspense
boundary.
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
, createRouteData(alias) function createRouteData<T, S = true>(fetcher: RouteDataFetcher<S, T>, options?: RouteDataOptions<undefined, S>): Resource<T | undefined> (+1 overload)
import createRouteData
} from "solid-start";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): Resource<Student[] | undefined>
() { return createRouteData(alias) createRouteData<Student[], true>(fetcher: RouteDataFetcher<true, Student[]>, options?: RouteDataOptions<undefined, true> | undefined): Resource<...> (+1 overload)
import createRouteData
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
}
export function Pagefunction Page(): JSX.Element
() { const studentsconst students: Resource<Student[] | undefined>
= useRouteData(alias) useRouteData<() => Resource<Student[] | undefined>>(): Resource<Student[] | undefined>
import useRouteData
<typeof routeDatafunction routeData(): Resource<Student[] | undefined>
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
, createRouteData(alias) function createRouteData<T, S = true>(fetcher: RouteDataFetcher<S, T>, options?: RouteDataOptions<undefined, S>): Resource<T | undefined> (+1 overload)
import createRouteData
} from "solid-start";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): Resource<Student[] | undefined>
() { return createRouteData(alias) createRouteData<Student[], true>(fetcher: RouteDataFetcher<true, Student[]>, options?: RouteDataOptions<undefined, true> | undefined): Resource<...> (+1 overload)
import createRouteData
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
}
export function Pagefunction Page(): JSX.Element
() { const studentsconst students: Resource<Student[] | undefined>
= useRouteData(alias) useRouteData<() => Resource<Student[] | undefined>>(): Resource<Student[] | undefined>
import useRouteData
<typeof routeDatafunction routeData(): Resource<Student[] | undefined>
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
} from "solid-start"; import { createServerData$(alias) const createServerData$: {
<T, S = true>(fetcher: RouteDataFetcher<S, T>, options?: RouteDataOptions<undefined, S> | undefined): Resource<T | undefined>;
<T, S = true>(fetcher: RouteDataFetcher<...>, options: RouteDataOptions<...>): Resource<...>;
}
import createServerData$
} from "solid-start/server";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): Resource<Student[] | undefined>
() { return createServerData$(alias) createServerData$<Student[], true>(fetcher: RouteDataFetcher<true, Student[]>, options?: RouteDataOptions<undefined, true> | undefined): Resource<...> (+1 overload)
import createServerData$
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
}
export function Pagefunction Page(): JSX.Element
() { const studentsconst students: Resource<Student[] | undefined>
= useRouteData(alias) useRouteData<() => Resource<Student[] | undefined>>(): Resource<Student[] | undefined>
import useRouteData
<typeof routeDatafunction routeData(): Resource<Student[] | undefined>
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
/routes/students.tsx
tsx
import { Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
, Accessor(alias) type Accessor<T> = () => T
import Accessor
, createResourceCreates a resource that wraps a repeated promise in a reactive pattern:
```typescript
// Without source
const [resource, { mutate, refetch }] = createResource(fetcher, options);
// With source
const [resource, { mutate, refetch }] = createResource(source, fetcher, options);
```
(alias) function createResource<T, R = unknown>(fetcher: ResourceFetcher<true, T, R>, options: InitializedResourceOptions<NoInfer<T>, true>): InitializedResourceReturn<T, R> (+3 overloads)
import createResource
} from "solid-js"; import { useRouteData(alias) const useRouteData: <T>() => MaybeReturnType<T>
import useRouteData
} from "solid-start"; import { createServerData$(alias) const createServerData$: {
<T, S = true>(fetcher: RouteDataFetcher<S, T>, options?: RouteDataOptions<undefined, S> | undefined): Resource<T | undefined>;
<T, S = true>(fetcher: RouteDataFetcher<...>, options: RouteDataOptions<...>): Resource<...>;
}
import createServerData$
} from "solid-start/server";
type Studenttype Student = {
name: string;
house: string;
}
= { name: string; house: string; }
export function routeDatafunction routeData(): Resource<Student[] | undefined>
() { return createServerData$(alias) createServerData$<Student[], true>(fetcher: RouteDataFetcher<true, Student[]>, options?: RouteDataOptions<undefined, true> | undefined): Resource<...> (+1 overload)
import createServerData$
(async () => { const response = await fetchfunction fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>
("https://hogwarts.deno.dev/students"); return await response.json(method) Body.json(): Promise<any>
() as Studenttype Student = {
name: string;
house: string;
}
[]; });
}
export function Pagefunction Page(): JSX.Element
() { const studentsconst students: Resource<Student[] | undefined>
= useRouteData(alias) useRouteData<() => Resource<Student[] | undefined>>(): Resource<Student[] | undefined>
import useRouteData
<typeof routeDatafunction routeData(): Resource<Student[] | undefined>
>();
return (
<ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> <Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
each(property) each: false | readonly Student[] | null | undefined
={studentsconst students: () => Student[] | undefined
()}> {(student(parameter) student: Student
) => <li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>{student(parameter) student: Student
.name}</li(property) JSX.IntrinsicElements.li: JSX.LiHTMLAttributes<HTMLLIElement>
>} </Forcreates a list elements from a list
it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned:
```typescript
<For each={items} fallback={<div>No items</div>}>
{(item, index) => <div data-index={index()}>{item}</div>}
</For>
```
If you have a list with fixed indices and changing values, consider using `<Index>` instead.
(alias) function For<T, U extends JSX.Element>(props: {
each: readonly T[] | undefined | null | false;
fallback?: JSX.Element;
children: (item: T, index: Accessor<number>) => U;
}): Accessor<U[]>
import For
> </ul(property) JSX.IntrinsicElements.ul: JSX.HTMLAttributes<HTMLUListElement>
> );
}
Lets try to understand when the routeData
is called and why you should setup resources (or use our helpers) inside it.
When rendering on the server, for each segment along the requested path, the routeData functions are called, parent-first. For example, for the following route structure:
sh
├── routes
│ ├── [house].tsx
│ ├── [house]
│ │ ├── index.tsx
│ │ ├── students.tsx
│ │ ├── students
│ │ │ ├── index.tsx
│ │ │ ├── year-[year].tsx
│ │ └── staff.tsx
sh
├── routes
│ ├── [house].tsx
│ ├── [house]
│ │ ├── index.tsx
│ │ ├── students.tsx
│ │ ├── students
│ │ │ ├── index.tsx
│ │ │ ├── year-[year].tsx
│ │ └── staff.tsx
When you visit /gryffindor/students
, the following routeData
functions are called, in this order:
/routes/[house].tsx
/routes/[house]/students.tsx
/routes/[house]/students/index.tsx
You can imagine what the router is doing. You don't have to write this code. It's pseudo-code to help you understand what's going on.
tsx
import { useLocation(alias) const useLocation: () => Location<unknown>
import useLocation
, useNavigate(alias) const useNavigate: () => Navigator
import useNavigate
} from "solid-start"; import {
defaultfunction HouseLayout(props: {
children?: JSX.Element;
}): JSX.Element
as HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getHouseLayoutData(alias) function getHouseLayoutData(args: RouteDataArgs): {}
import getHouseLayoutData
} from './routes/[house]';
import {
defaultfunction StudentsLayout(props: {
children?: JSX.Element;
}): JSX.Element
as StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getStudentsLayoutData(alias) function getStudentsLayoutData(args: RouteDataArgs): {}
import getStudentsLayoutData
} from './routes/[house]/students';
import {
defaultfunction Students(): JSX.Element
as Students(alias) function Students(): JSX.Element
import Students
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getStudentsData(alias) function getStudentsData(args: RouteDataArgs): {}
import getStudentsData
} from './routes/[house]/students/index';
function Routesfunction Routes(): JSX.Element
() { const argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
= { location(property) location: Location<unknown>
: useLocation(alias) useLocation(): Location<unknown>
import useLocation
(), navigate(property) navigate: Navigator
: useNavigate(alias) useNavigate(): Navigator
import useNavigate
(), params(property) params: {
house: string;
}
: { house: 'gryffindor' } }
const houseLayoutDataconst houseLayoutData: {}
= getHouseLayoutData(alias) getHouseLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getHouseLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: null }); const studentsLayoutDataconst studentsLayoutData: {}
= getStudentsLayoutData(alias) getStudentsLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getStudentsLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: houseLayoutDataconst houseLayoutData: {}
}); const studentsData = getStudentsLayoutData(alias) getStudentsLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getStudentsLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: studentsLayoutDataconst studentsLayoutData: {}
});
return (
<RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: houseLayoutDataconst houseLayoutData: {}
}}> <HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
> <RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: studentsLayoutDataconst studentsLayoutData: {}
}}> <StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
> <RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: studentsData }}> <Students(alias) function Students(): JSX.Element
import Students
/> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> </StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> </HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> )
}
tsx
import { useLocation(alias) const useLocation: () => Location<unknown>
import useLocation
, useNavigate(alias) const useNavigate: () => Navigator
import useNavigate
} from "solid-start"; import {
defaultfunction HouseLayout(props: {
children?: JSX.Element;
}): JSX.Element
as HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getHouseLayoutData(alias) function getHouseLayoutData(args: RouteDataArgs): {}
import getHouseLayoutData
} from './routes/[house]';
import {
defaultfunction StudentsLayout(props: {
children?: JSX.Element;
}): JSX.Element
as StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getStudentsLayoutData(alias) function getStudentsLayoutData(args: RouteDataArgs): {}
import getStudentsLayoutData
} from './routes/[house]/students';
import {
defaultfunction Students(): JSX.Element
as Students(alias) function Students(): JSX.Element
import Students
, routeDatafunction routeData(args: RouteDataFuncArgs<unknown>): {}
as getStudentsData(alias) function getStudentsData(args: RouteDataArgs): {}
import getStudentsData
} from './routes/[house]/students/index';
function Routesfunction Routes(): JSX.Element
() { const argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
= { location(property) location: Location<unknown>
: useLocation(alias) useLocation(): Location<unknown>
import useLocation
(), navigate(property) navigate: Navigator
: useNavigate(alias) useNavigate(): Navigator
import useNavigate
(), params(property) params: {
house: string;
}
: { house: 'gryffindor' } }
const houseLayoutDataconst houseLayoutData: {}
= getHouseLayoutData(alias) getHouseLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getHouseLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: null }); const studentsLayoutDataconst studentsLayoutData: {}
= getStudentsLayoutData(alias) getStudentsLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getStudentsLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: houseLayoutDataconst houseLayoutData: {}
}); const studentsData = getStudentsLayoutData(alias) getStudentsLayoutData(args: RouteDataFuncArgs<unknown>): {}
import getStudentsLayoutData
({ ...argsconst args: {
location: Location<unknown>;
navigate: Navigator;
params: {
house: string;
};
}
, data(property) RouteDataFuncArgs<unknown>.data: unknown
: studentsLayoutDataconst studentsLayoutData: {}
});
return (
<RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: houseLayoutDataconst houseLayoutData: {}
}}> <HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
> <RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: studentsLayoutDataconst studentsLayoutData: {}
}}> <StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
> <RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
value(property) value: {
data: any;
}
={{ data: studentsData }}> <Students(alias) function Students(): JSX.Element
import Students
/> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> </StudentsLayout(alias) function StudentsLayout(props: ParentProps): JSX.Element
import StudentsLayout
> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> </HouseLayout(alias) function HouseLayout(props: ParentProps): JSX.Element
import HouseLayout
> </RouteContextconst RouteContext: Context<{
data: any;
}>
.Provider(property) Context<{ data: any; }>.Provider: ContextProviderComponent<{
data: any;
}>
> )
}
When rendering on the client, the routeData function is called when the route is first rendered. This is because the routeData function is called with the current route parameters.
This is a great way to load data, but it can be a bit verbose. SolidStart provides a few hooks to make this easier.