Skip to content

Latest commit

 

History

History
151 lines (105 loc) · 5.38 KB

BOILERPLATE.md

File metadata and controls

151 lines (105 loc) · 5.38 KB

Using as a Boilerplate

This project can be easily turned into a production-ready boilerplate by following the next steps:

Installation

git clone https://github.com/theninthsky/client-side-rendering.git
npm i
npm run boilerplatify

Adjustments

Sitemap

To automatically generate a sitemap, replace the https://example.com hostname in create-sitemap.js with your own hostname.
To handle dynamic routes, modify the script to first fetch the dynamic routes and then add them to the paths array.

Here is an example: https://github.com/theninthsky/client-side-rendering/blob/main/scripts/create-sitemap.js

Static Assets

Static assets can be fetched and stored in the CDN during the build process, allowing them to be served very quickly.
The asset generation process should take place in fetch-static.js.

Here is an example that fetches static data a stores it under public/json: https://github.com/theninthsky/client-side-rendering/blob/main/scripts/fetch-static.js

Features

Abiding by this boilerplate's requirements grants you the following features:

Requirements

Tools that optimize for performance usually require the developer to follow a set of rules to "help" them do it.

For example: Next.js and Remix require you to use their file-based routers in order to be able to apply automatic code-splitting and other optimizations, and modern frameworks require the use of signals (which sometimes feel a little unintuitive) to skip full-tree rerenders.

This boilerplate is no exception, thus it requires two things in order to fully apply its optimizations:

  1. Lazily loading all pages and giving them unique names.
  2. Maintaining the pages-manifest, a file that specifies the chunk names, paths and data to preload.

Naming Async Chunks (Pages)

Async chunks can be easily named using Webpack's magic comments:

App.tsx

const Home = lazy(() => import(/* webpackChunkName: 'home' */ 'pages/Home'))
const LoremIpsum = lazy(() => import(/* webpackChunkName: 'lorem-ipsum' */ 'pages/LoremIpsum'))

This will create both home.[hash].js and lorem-ipsum.[hash].js files (instead of the default, cryptic, [id].[hash].js files).

The Pages Manifest File Structure

To best describe what properties should be in the pages-manifest file, we'll use a TypeScript-like definition:

pages-manifest.js

[
  {
    chunk: string
    path: string
    data?:
      {
        url: string | ((params: { [x: string]: string }) => string)
        crossorigin?: string
        preconnectURL?: string
      }[]
  }
]

chunk is the unique page name we chose via magic comments. The name "main" cannot be used.

path is the pathname of the page (like /, /about, /posts...).

data can be supplied if the page has dynamic data that will be fetched right when it loads.

url is the data API URL. Can be a string or a function that receives the dynamic path params and returns a string.

For example:

{
    chunk: 'products',
    path: '/products/:category/:page',
    data: {
      url: ({ category, page }) => `https://www.my-api.com/products/${category}/${page}`
    }
  }

crossorigin is for CORS-enabled fetches.

preconnectURL is for cases where the fetch request will be followed by requests to resources from a different origin.

Recommendations

Styling

You should use @emotion/css as your styling solution.

This package is the perfect balance between potential and performance, between "styled components" and CSS modules.
It can even be used to mimic the API of CSS modules and thus allow for easy migration:

import { css, cx } from '@emotion/css'

import Back from './Back'

const Title = ({ className, back, children, ...otherProps }) => {
  return (
    <div {...otherProps}>
      {back && <Back className={style.back} />}

      <h1 className={cx(style.wrapper, className)}>{children}</h1>
    </div>
  )
}

const style = {
  wrapper: css`
    font-weight: 500;
    color: var(--primary-color);
  `,
  back: css`
    margin-right: 20px;
  `
}

export default Title

Note that if you choose to use CSS modules instead, you should NOT extract CSS to separate files (typically done by mini-css-extract-plugin).
Despite the fact that the advantages of this extraction are mostly negligible nowadays, when used together with code-splitting, it will produce severe styling override issues with shared components.