This is a boilerplate for a multi-page web application (MPA) that runs entirely in a service worker context, demonstrating how to serve dynamically generated HTML.
Similar to Next.js, routes are defined as a directory
structure under src/app
.
Dynamic routes are defined by using square brackets in the folder name. For
example, if you want to create a dynamic route for user profiles, you can create
a folder with a path of src/app/profiles/[id]
.
Each route folder should have a page.tsx
inside. (Unlike Next.js, there is no
layout.tsx
file.)
The initial page HTML is rendered by React-DOM/Server and gets hydrated by React-DOM/Client.
Static files in the public
directory are cached and served by the service
worker.
- Clone this starter kit
git clone https://github.com/michaelcpuckett/sw-app-router-starter
- Install dependencies
npm install
- Run the
app-router
script to generate list of routes and static files.
npm run app-router
- Build the project.
npm run build
- Serve the project.
npm run serve
- Open in the browser.
http://localhost:8080
This file serves static files and routes requests to the appropriate React components, utilizing the underlying ExpressWorker framework.
This file hydrates the React components on the rendered pages using
react-dom/client
.
This component wraps Page Components, injecting metadata and initial data into the HTML.
The app-router
script generates the route and static files configuration
required by the service worker. It scans the app directory for pages and the
public directory for static files.
To add a new page:
-
Create a new folder in the
app
directory and create apage.tsx
file inside it. -
Define the React component for the page as the
default
export. -
Define and export
getStaticProps
andmetadata
. (See below.) -
Run
npm run app-router
to regenerate the routes.
getStaticProps
is a function used to fetch data at render time. It allows you
to fetch data from an API or database and pass it as props to the page
component. The path params are passed to this function. You can define
getStaticProps
as follows:
export const getStaticProps: GetStaticProps = async function ({
params: { id },
}) {
const data = await fetchData({ id });
return {
props: {
data,
},
};
};
metadata
is an object that contains information about the page, such as the
title and description. You can define metadata
as follows:
export const metadata: Metadata = {
title: 'Page Title',
description: 'Page description',
};
If you need to access the route params, you can export a function instead:
export const metadata: GetMetadata = ({ params: { id } }) => ({
title: 'Note ' + id,
});
The default
export should be the Page component. It will receive the props
defined in getStaticProps
. It will be wrapped in the PageShell
.
export default function HomePage({ data }: { data: Data }) {
return <main>{data.foo}</main>;
}
Use npm run serve
and npm run dev
during development.
For easiest debugging, in the Web Inspector, under the Application tab, under Service Workers, select the checkbox for "Update on reload".
Styles can be edited directly in public/styles.css
.
Use npm run build
to generate a production build.
The built-in strategy for invalidating the old cache and serving the updated
content is through incrementing the version in public/cache.json
.
Routes can pre-cache or inline key assets. This allows navigation between pages to be nearly instantaneous.
The server only needs to be able to serve the initial static assets. This project uses Firebase Static Hosting.
When a user navigates to a route while offline, the service worker can serve a previously cached response or generate a new one based on stored data.
Pages generated by a service worker aren't indexed by search engines. You may need alternative strategies for SEO, such as generating static HTML snapshots or using a separate build process to create server-rendered pages.
This project is licensed under the MIT License.