npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-broker

v2.0.5

Published

Component lazy-loading + code splitting that works with SSR, Webpack 4, and Babel 7

Downloads

232

Readme

Wolf of Wall Street gif

react-broker

A lightweight library for lazy components using React 16.8+. It's perfect for code splitting and has the simplest SSR story you've ever seen out-of-the-box.

Critically, this package is only intended to work with Webpack, specifically Webpack 4 and future versions. There are no plans to implement a design accommodating Parcel or other bundlers. There is also a hard requirement for babel-plugin-macros (which is shipped with CRA) if you opt to use the macro.

import {BrokerProvider} from 'react-broker'
import lazy from 'react-broker/macro'

// Automatically generates dynamic imports for webpack with babel-plugin-macros. 
// Just give it the path.
const LazyPage = lazy('../pages/Page', {loading: props => 'Loading...'})

////////////////////////////////////////////////////////////////////////////////
//                               ⬇ BECOMES ⬇                                //
///////////////////////////////////////////////////////////////////////////////

const LazyPage =
  require('react-broker').lazy(
    'src/pages/Page',
    () => import(/* webpackChunkName: "src/pages/Page" */ '../pages/Page'),
    {loading: props => 'Loading...'}
  )
  
function App () {
  // Look at me! I'm used like a normal component.
  return (
    <BrokerProvider>
      <LazyPage id='1'/>
    </BrokerProvider/>
   )
}

Requirements

  • Webpack 4+ (because chunks)
  • React 16.8+ (because hooks)
  • Babel (because babel-plugin-macros)

Installation

yarn add react-broker or npm i react-broker

Examples

Hello world Hello world w/ Router


Documentation

react-broker/macro

The function that transforms your imports and delegates your async components.

import lazy from 'react-broker/macro'

lazy(component <String>, options <Object>)

component {String} A path to a React component you want to lazy load. The component must be in the default export of the file.

Paths cannot be passed via an identifier, it has to be a plain string. It is used just like a regular component.

You may also lazy load external library components, but just know that the component in question must be the default export.

// Used like a regular component
const LazyPage = lazy('./pages/Home', {loading: props => 'Loading ${props.id}...'})

<LazyPage id={1}>
  // ...
</LazyPage>

options {Object}

  • loading (props, context{retry, error})
    • props props passed the component
    • context
      • retry is a function which will force a reload of the component
      • error is any error returned by Promise.reject, this is only relevant if an error component isn't also defined in options
  • error (props, context{retry, error})
    • See loading

Broker.Provider

Manages code-splitting and the resolution of your async components by keeping track of which chunk names have been loaded and also determining which <scripts> need to be included from the server-side. Broker.Provider must be defined at the top-level of your lazy loaded components.

Props

chunkCache {Broker.createChunkCache}

You only provide a chunkCache on the server side. In the client it is not allowed. The chunk cache is used for tracking which chunks were loaded during the latest render phase of the app. Broker.createChunkCache

import * as Broker from 'react-broker'

const chunkCache = Broker.createChunkCache()

function App (props) {
  return (
    <Broker.Provider chunkCache={chunkCache}>
      <LazyPage id={props.id}/>
    </Broker.Provider>
  )
}

Broker.createChunkCache

Creates a context for Broker.Provider to track chunks in and provides helper methods to provide access to those chunks.

createChunkCache.getChunkNames()

Returns an array of all the Webpack chunk names loaded into the current app.

createChunkCache.getChunks(webpackStats)

Returns a Set of all the Webpack chunks loaded into the current app.

  • webpackStats <Object>
    • The stats object created by Webpack.
createChunkCache.getChunkScripts(webpackStats, options)

Returns a string representation of all the <script> tags to include in the output of your app when using with SSR.

  • webpackStats {Object}
    • The stats object created by Webpack.
  • options {Object}
    • preload {Bool|Object}
      • If true, this will generate <link rel='preload'> tags with your scripts.
      • If an object, the key/value pairs will be added to the <link rel='preload'> tags as attributes. e.g. {preload: {crossorigin: 'anonymous'}} generates <link rel='preload' as='script' crossorigin='anonymous' href='...'>
    • async {Bool}
      • If true, an async flag will be added to your <script> tags
      • default true
    • defer {Bool}
      • If true, a defer flag will be added to your <script> tags and async will be omitted
      • default false

See the SSR section for an example


Broker.lazy

This is the function created by react-broker/macro.

To skip the macro you could do something like this with the Webpack code-splitting API:

import {lazy} from 'react-broker'
const Component = lazy(
  'uniqueChunkName', 
  () => import(/* webpackChunkName: "uniqueChunkName" */'./path/to/component'), 
  {loading: props => 'Loading...'}
)

Lazy.load()

Preloads the component.

const LazyPage = lazy('./pages/Home')
// ...
<Link onMouseEnter={LazyPage.load}>
  Home
</Link>

Broker.load(...components <String>)

Preloads one or several Lazy components.

import * as Broker from 'react-broker'
import lazy from 'react-broker/macro'

const LazyA = lazy('./A')
const LazyB = lazy('./B')

Broker.load(LazyA, LazyB).then(/*...*/)

Broker.loadAll(

App: React.Element,

renderer: ReactDOM.renderToStaticMarkup|renderToString

)

Tracks all of the chunks used in your app during the server side render and optionally renders your app to a string

  • App {React.Element}
    • Your React application
  • renderer {ReactDOM.renderToStaticMarkup|ReactDOM.renderToString}
    • default ReactDOM.renderToStaticMarkup
    • The renderer used for determining the chunks used in your app. To avoid, extra renders, you could change this to `ReactDOM.renderToString.

See the SSR section for an example


Broker.loadInitial(chunkCache: Broker.createChunkCache)

Populates your chunk cache with the async components present in your application. This requires that Broker.getChunkScripts was used on the server side. The primary use case for this function is elimination loading components and flashes when initially rendering your app in the browser.

See the SSR section for an example


Server-side Rendering

client/render.js

import React from 'react'
import ReactDOM from 'react-dom'
import * as Broker from 'react-broker'
import App from '../App'


const app = (
  <Broker.Provider>
    <App/>
  </Broker.Provider>
)

Broker.loadInitial().then(
  () => ReactDOM.hydrate(app, document.getElementById('⚛️'))
)

server/render.js

import React from 'react'
import ReactDOMServer from 'react-dom/server'
import express from 'express'
import * as Broker from 'react-broker'
import App from '../App'


export default function createRenderer({
  // These are the Webpack compilation stats returned by Webpack post-run.
  // https://webpack.js.org/api/stats/
  clientStats
}) {
  app = express()

  app.get('*', /* Note 'async' here */ async (req, res, next) => {
    res.set('Content-Type', 'text/html')
    // keeps track of lazy chunks used by the current page
    const chunkCache = Broker.createChunkCache()
    // chunkCache is passed to Broker.Provider as a prop
    const app = (
      <Broker.Provider chunkCache={chunkCache}>
        <App/>
      </Broker.Provider>
    )
    // Preloads the async components and renders the app to a string
    const page = await Broker.loadAll(app, ReactDOMServer.renderToString)
    // You could also do this if you have other requirements in addition to preloading with
    // react-broker
    // await Broker.loadAll(app, ReactDOMServer.renderToStaticMarkup)
    // const page = await ReactDOMServer.renderToString(app)
    
    // Generates <script> and <preload> tags for this page
    const chunks = chunkCache.getChunkScripts(clientStats, {preload: true})

    res.send(`
      <html>
        <head>
          ${chunks.preload}
        </head>
        <body>
          <div id='⚛️'>
            ${app}
          </div>
          ${chunks.scripts}
        </body>
      </html>
    `)
  })

  return app
}