A simple framework inspired by Next.js to create Server and Statically Rendered React Apps.
Read the blog post detailing the process of ideation and creation of this project.
- Starting An Isomorph Project
- Required Project Structure
- Pages Structure and Data Fetching
- Handling Page Meta Data
- Determining When your page is on the client-side
- Environment Variables
- Custom Error Pages
- Building and Serving to Production
- Supporting dynamic page routes.
- Server Side Rendering with data fetching on the server.
- Static Page Generation and serving with revalidation and
Stale-While-Revalidate
approach of handling serving. - Server Side data fetching utilities like
getDataOnServer
andgetStaticProps
and function to populate page meta data withgetPageMeta
. - Full Client-Side Rendering/Hydration of pages to enable user interactivity and events.
useInitialData
hook to access initial data fetched on the server across the entire component chain.- All Environment variables accessible on server-side and variables starting
ISOMORPH_PUBLIC_...
accessible on the client-side. - Minification and Tree-Shaking of code in Production mode.
- Bundle Caching in Production mode to enable super-fast load times for both server-rendered and statically generated pages post first build.
- Custom error pages using
_error
for handling404
s and500
s.
The simplest way to get started with an isomorph project is using create-isomorph-app
.
npm i -g isomorph-web
npx create-isomorph-app [project directory] [?project name]
// ex:
npx create-isomorph-app my-isomorph-project "Isomorph App"
As of now to setup an isomorph project, create a directory for your project. I'm assuming you have npm and Node.js already installed.
mkdir my-isomorph-project
cd ./my-isomorph-project
npm init -y
Once done with this, install isomorph-web
using npm or yarn.
yarn add isomorph-web
// or
npm i --save isomorph-web
Create an src
folder, this will house all your components, utils and project related JavaScript code. Inside it create a pages
folder that will house your page files and have React components associated with them.
Check the Required Project Structure section to know the sample structure of isomorph projects.
Update your package.json file's scripts
to the following:
"scripts": {
+ "build": "isomorph-web build",
+ "dev": "isomorph-web dev",
+ "start": "isomorph-web start"
}
Once done, run the dev server using:
npm run dev
Your pages have to be housed inside the src/pages
folder of your project directory.
.
โโโ src/
โ โโโ pages/
โ โ โโโ index.js
โ โ โโโ static-page.js
โ โโโ components/
โ โ โโโ ...
โ โโโ utils/
โ โโโ ...
โโโ package.json
I'm currently working on adding dynamic route support soon.
Pages are simple React component files that expose a default React component export, along with a few other exports to facilitate data fetching and meta data handling.
// No need to 'import React from "react";', it's always in scope.
import useInitialData from "isomorph-web/package/hooks/useInitialData";
const PageComponent = () => {
const initialData = useInitialData(); // Use this hook to access data fetched for the page in getPropsOnServer/getStaticProps.
return (
<>
Data received from server: {initialData.variable}. I can even have
environment variables: {process.env.ISOMORPH_PUBLIC_ENV_VAR}.
</>
);
};
export default PageComponent;
// For server-rendered pages, for static pages, use getStaticProps or skip both of these data fetcher functions.
export const getPropsOnServer = async (context) => {
console.log(
context.req,
context.res,
context.cookies, // Parsed cookie object for you
context.env, // Environment variables, would not be exposed to the client.
context.url // Request url path.
context.query // Request query parameters
);
const variable = await getVariableFromDatabase(context.url, context.cookies);
return { variable }; // This will be passed to the page component and can be accessed using the `useInitialData` hook.
};
// For static pages
export const getStaticProps = async (context) => {
console.log(
context.env, // Environment variables, would not be exposed to the client.
context.url // Request url path.
);
return { revalidate: 3 * 60, variable: 5 }; // Revalidate the page after 3 minutes, pass the rest of the payload as initial data.
};
What's server-side and static rendering without SEO benefits. For that purpose, we have the getPageMeta
function that will allow you to add all meta
tags, link
tags, script
tags you need.
// In your page file.
// src/pages/index.js
...
export const getPageMeta = () => {
return {
title: "Home Page",
meta: [
{
name: "description",
content: "A simple page generated on the server-side.",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1.0",
},
],
links: [
{
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css",
},
],
scripts: [
{
type: "application/ld+json",
content: '{ type: "Your JSON LD data for rich results" }',
},
],
};
};
Components rendered on the client-side are hydration-error free, that's because they are not hydrated in the first place, they are re-rendered, which means as long as your pages are not super heavy and big, you can avoid worrying about errors like HTML on server did not match the HTML on client
which can be frustrating and error-prone when writing React apps that render and function on both the client and the server-side without any impact on page load and performance.
To find out whether your app is on the client-side, just use the following:
typeof window === "undefined"; // If this is true, you're on the server-side.
Environment variables are fully supported, use a .env
file at the root of your folder to expose environment variables for your app. On the server-side and while rendering your page components on the server-side, you have access to all environment variables.
On the client-side, all environment variables starting with ISOMORPH_PUBLIC_
are automatically exposed.
For handling custom UIs for 404 and 500 errors, you can create a pages/_error.js
file.
const ErrorComponent = ({ statusCode, error }) => {
// statusCode -> 404 | 500
// error -> The error message that was received from the server.
return (
<>
<h4>{statusCode}</h4>
<br />
{error}
</>
);
};
export default ErrorComponent;
Use the following command to compile your source code to production config.
npm run build
Use the following to start the server.
npm run start
This project is still a WIP and will be for the near future, check the Work In Progress section to see what's still pending.