Loaders

Each route in the routes configuration can provide a loader to provide the route component with data.

function todosLoader() {
	return [
		{ id: 1, title: 'cleaning' },
		{ id: 2, title: 'shopping' },
	];
}

let Router = Routes(
	<>
		<Todos path="todos" loader={todosLoader} />
	</>,
);

In the route component, the data can be used with the useLoaderResult hook.

function Todos() {
	let todos = useLoaderResult()
		...
}

More commonly the loader would fetch data from some backend api or service:

async function todosLoader() {
	let response = await fetch('/api/todos.json');
	let todos = await response.json();

	return todos;
}

It is also allowed to return a Response and the router will extract the response data for you.

function todosLoader() {
	return fetch('/api/todos.json');
}

Loaders have access to the url, params, and splat if you need them.

let Router = Routes(
	<>
		<Todo path="todos/:todoId" loader={todoLoader} />
	</>,
);

function todoLoader({ url, params, splat }) {
	return fetch(`/api/todos/${params.todoId}`);
}

Suspense

While the loader is busy, useLoaderResult will suspend your component. Use react suspense boundaries somewhere up in the component tree to display a fallback user interface, like a loading indicator.

let Router = Routes(
	<App>
		<Todo path="todos/:todoId" loader={todoLoader} />
	</App>,
);

function App(props) {
	return <Suspense fallback="loading...">{props.children}</Suspense>;
}

function Todo() {
	let todo = useLoaderResult();
		...
}

Errors

When the loader has thrown an error, useLoaderResult will throw the error during render. Use react error boundaries somwhere up in the component tree to display an error handling user interface.

let Router = Routes(
	<App>
		<Todo path="todos/:todoId" loader={todoLoader} />
	</App>,
);

function App(props) {
	return <ErrorBoundary>{props.children}</ErrorBoundary>;
}

function Todo() {
	let todo = useLoaderResult();
		...
}

class ErrorBoundary extends React.Component {
	constructor(props) {
		super(props);
		this.state = { error: undefined };
	}

	static getDerivedStateFromError(error) {
		return { error }
	}

	render() {
		if (this.state.error) {
			return "Something went wrong";
		}

		return this.props.children;
	}
}