The `useLoaderData` hook
Frontend and backend code
Section titled โFrontend and backend codeโIn a typical website, code can be split into two main parts:
-
Frontend code
- runs in the userโs browser (the โclientโ)
- written in HTML, CSS and JavaScript
- responsible for
- rendering the website
- handling user interactions
- sending HTTP requests to the server
-
Backend code:
- runs on the server.
- written in JavaScript
- responsible for
- database fetching and updates
- authentication and authorisation
- providing HTTP responses to the client

The loader function
Section titled โThe loader functionโIn Remix, front and backend code is combined into a single file called a route module.
The file we have been working in - root.tsx - is an example of this.
Take another look at the app/root.tsx file:
import { useLoaderData } from 'react-router'import { type Route } from './+types/root.ts'import { type loader } from './__root.server.tsx'import { GeneralErrorBoundary } from './components/error-boundary.tsx'import Document from './components/shared-layout/Document.tsx'import { useNonce } from './utils/nonce-provider.ts'import rootLinkElements from './utils/providers/rootLinkElements.ts'
export const links: Route.LinksFunction = () => { return rootLinkElements}export { meta } from './__root.client.tsx'export { headers, loader } from './__root.server.tsx'
export default function App() { const data = useLoaderData<typeof loader | null>() const nonce = useNonce()
return ( <Document nonce={nonce} honeyProps={data?.honeyProps}> <div className="flex h-screen flex-col justify-between"> <div className="flex-1"> <main className="grid h-full place-items-center"> <h1 className="text-mega">Welcome to Epic News!</h1> </main> </div> </div> </Document> )}
export const ErrorBoundary = GeneralErrorBoundaryYouโll notice that we are exporting a function from this file called: loader.
export { headers, loader } from './__root.server.tsx'This function is responsible for fetching data from the server and processing it before it is sent to the frontend.
But where is this function defined? The line highlighted above says that the loader function is being exported somehow, but we donโt see them in this file. ๐ง
Thatโs because it is defined in a completely separate file: __root.server.tsx.
If you are interested, open up the __root.server.tsx file and take a look at the loader function. Youโll see that it is responsible for fetching data from the server and sending it to the frontend.
We will be looking more closely at the loader function in a later tutorial.
The useLoaderData hook
Section titled โThe useLoaderData hookโuseLoaderData is a custom React hook supplied for us by Remix.
Here, it is being used to pass the data from the loader function in __root.server.tsx into the App component running in the browser.
We will look more at this hook later in the module, but for now lets use another hook to get our theme switch working.
Adding the theme switch
Section titled โAdding the theme switchโMaking sure you are still inside app/root.tsx, letโs start by updating the import statements at the top of the file.
Add the code highlighted in green to the end of your import statements in app/root.tsx:
import { type LinksFunction } from '@remix-run/node'import Document from '~/components/shared-layout/Document'import { useNonce } from '~/utils/nonce-provider.ts'import rootLinkElements from '~/utils/providers/rootLinkElements'import { ThemeSwitch, useTheme } from './routes/resources+/theme-switch.tsx'Next, carefully add the code highlighted in green below to the App component function itself:
14 collapsed lines
import { useLoaderData } from 'react-router'import { type Route } from './+types/root.ts'import { type loader } from './__root.server.tsx'import { GeneralErrorBoundary } from './components/error-boundary.tsx'import Document from './components/shared-layout/Document.tsx'import { useNonce } from './utils/nonce-provider.ts'import rootLinkElements from './utils/providers/rootLinkElements.ts'import { ThemeSwitch, useTheme } from './routes/resources+/theme-switch.tsx'
export const links: LinksFunction = () => { return rootLinkElements}export { meta } from './__root.client.tsx'export { headers, loader } from './__root.server.tsx'
export default function App() { const data = useLoaderData<typeof loader | null>(); const nonce = useNonce(); const theme = useTheme()
return ( <Document theme={theme} nonce={nonce} honeyProps={data?.honeyProps}> <div className="flex h-screen flex-col justify-between"> <div className="flex-1"> <main className="grid h-full place-items-center"> <h1 className="text-mega">Welcome to Epic News!</h1> </main> </div>
<div className="container flex justify-between pb-5"> <ThemeSwitch userPreference={data?.requestInfo.userPrefs.theme} /> </div> </div> </Document> )}When you are ready, open the explanation below.
1. The useTheme hook
Section titled โ1. The useTheme hookโuseTheme is a custom React hook (i.e. a function that can only be used inside a React component).
Itโs a function that:
- Figures out what theme the user wants (light mode or dark mode)
- Returns that theme as a string (either
'light'or'dark') - Stores the result in the
themevariable
const theme = useTheme()2. How is the theme Variable Used?
Section titled โ2. How is the theme Variable Used?โData Type: The theme variable is a string.
Possible Values:
'light'- Light mode (light background, dark text)'dark'- Dark mode (dark background, light text)
Example:
// If the user prefers dark mode:const theme = useTheme() // theme = 'dark'
// If the user prefers light mode:const theme = useTheme() // theme = 'light'The theme variable is passed to the Document component:
<Document theme={theme} nonce={nonce} honeyProps={data?.honeyProps}> {/* ... rest of the app ... */}</Document>- Weโre passing the
themevalue as a prop (property) to theDocumentcomponent - The
Documentcomponent will use this value to apply the correct styling to the entire page
3. The ThemeSwitch Component
Section titled โ3. The ThemeSwitch ComponentโA few lines down, we pass this into the ThemeSwitch component:
<ThemeSwitch userPreference={data?.requestInfo.userPrefs.theme} />The ThemeSwitch component is a button that lets users change the theme of the website.
User Experience:
- User sees an icon (sun, moon, or laptop)
- User clicks the icon
- The theme changes (light โ dark โ system โ light โ โฆ)
- The icon updates to show the current mode
The Three Modes:
- Light mode (โ๏ธ sun icon): Everything is light-colored
- Dark mode (๐ moon icon): Everything is dark-colored
- System mode (๐ป laptop icon): Matches your computer/phoneโs theme setting
The flow when user clicks
Section titled โThe flow when user clicksโ1. User clicks the button โ2. Form submits with nextMode value (e.g., 'dark') โ3. Request sent to /resources/theme-switch โ4. Server action runs (saves theme in cookie) โ5. useOptimisticThemeMode detects the change immediately โ6. useTheme returns the new theme โ7. Document component gets new theme โ8. Page updates with new colors!Compare your notes with the explanation above. Where there are differences, make a note of these.
Testing the light and dark mode toggle button
Section titled โTesting the light and dark mode toggle buttonโSave your changes, then head back to the browser. You should now be able to toggle the theme between light and dark mode by clicking the button:

๐ Congratulations! ๐
Section titled โ๐ Congratulations! ๐โYou now have a working light and dark mode toggle button in your Remix app! ๐
Automatically organise imports in VS Code
Section titled โAutomatically organise imports in VS CodeโFinally, lets fix the yellow underlines that might still be showing under your new import statements.
As mentioned earlier, VS Code is warning us that we havenโt used the values, variables and functions that we are importing yet.
Thankfully, VS Code provides us with a keyboard shortcut to automatically organise our imports in the correct order.
- Windows: SHIFT + ALT + O
- Mac: SHIFT + OPTION + O
Press this shortcut now to automatically organise your imports at the top of your file:

Remember to save your file after you have done this, and commit your changes to Git.
๐ Assignment documentation
Section titled โ๐ Assignment documentationโYou have now completed the implementation of the light and dark mode toggle button in your Remix app.

Itโs time to start the report you will be submitting that documents the process of building the app.
If you havenโt already, create a new Word document in your OneDrive folder and give it a suitable name, e.g. Building a news app with Remix.
Remember to include the following parts from this tutorial in your report:
Setting up light and dark mode
Section titled โSetting up light and dark modeโ- What is โlightโ and โdarkโ mode in a web app?
- Why is light and dark mode important in a web app?
- What accessibility considerations should be taken into account when implementing light and dark mode?
- What is the
useLoaderDatahook responsible for in a Remix app? - Show how you have used the
useLoaderDatahook in your app to update thethemepreference in theThemeSwitchcomponent. - Describe what React props are and how they are used to pass data into a component.
- How are React props used in the
Documentcomponent to control the theme of the Epic News website? Include:- The role of TypeScript interfaces in defining the shape of an object, and how this is used in the
DocumentPropsinterface in theDocumentcomponent. - How string interpolation is used to insert the value of a variable into a string in JavaScript, and how this is utilized in the
Documentcomponent to control the theme of the Epic News website.
- The role of TypeScript interfaces in defining the shape of an object, and how this is used in the
- How did you test the light and dark mode toggle button in the browser?
Support your explanations with screenshots and code snippets where necessary.
Whatโs next?
Section titled โWhatโs next?โIn the next step, we will add a navbar and footer to the website to introduce navigation and improve user experience.