Socket
Socket
Sign inDemoInstall

react-streaming

Package Overview
Dependencies
2
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    react-streaming

React 18 Streaming. Made fully-fledged & easy.


Version published
Weekly downloads
6.3K
decreased by-25.47%
Maintainers
1
Install size
56.1 kB
Created
Weekly downloads
 

Readme

Source

react-streaming

React 18 Streaming. Made fully-fledged & easy.

Contents

  • Intro
  • Why Streaming
  • Get Started
  • Get Started (Library Authors)

Intro

Features (for React users):

  • Unlocks <Suspsense> for SSR apps.
  • Unlocks React libraries of tomorrow. (Such as using Telefunc for data fetching.)
  • Supports all platforms (Vercel, Cloudflare Workers, AWS, Netlify Edge, Deno, Deploy, ...).
  • Two SEO strategies: conservative or google-speed.
  • Bonus: new useAsync() hook.

Features (for library authors):

  • useSsrData(): Define isomorphic data.
  • injectToStream(): Inject chunks to the stream.

Easy:

import { renderToStream } from 'react-streaming/server'

const {
  pipe, // Node.js (Vercel, AWS)
  readable // Edge Platforms (Coudflare Workers, Netlify Edge, Deno Deploy)
} = await renderToStream(<Page />)

Why Streaming

React 18's new SSR streaming architecture unlocks many capabilities:

  • Data Fetching:
    • Use RPC to fetch data in a seamless way, e.g. with Telefunc or tRPC. (Data fetching SSR hooks will be a thing of the past: no more Next.js's getServerSideProps() nor vite-plugin-ssr's onBeforeRender().)
    • Expect your GraphQL tools to significantly improve, both performance and DX. (Also expect new tools such as Vilay.)
  • Fundamentally improved mobile performance. (Mobile users can progressively load the page as data is fetched, before even a single line of JavaScript is loaded. Especially important for low-end and/or poorly-connected devices.)
  • Progressive Hydration. (Page is interactive before the page/data finished loading.)

The problem: The current React 18 Streaming architecture is low-level and its ergonomics are cumbersome. (E.g. there is no standard way for library authors to take advantage of the new streaming architecture.)

The solution: react-streaming.


Get Started

  1. Install

    npm install react-streaming
    
  2. Server-side

    import { renderToStream } from 'react-streaming/server'
    
    // Cross-platform
    const {
      pipe, // Defined if running in Node.js, otherwise `null`
      readable // Defined if running in Edge Platforms (.e.g. Coudflare Workers), otherwise `null`
    } = await renderToStream(<Page />, options)
    
  3. Client-side

    import { ReactStreaming } from 'react-streaming/client'
    // Wrap your root component `<Page>` (aka `<App>`) with `<ReactStreaming>`
    const page = (
      <ReactStreaming>
        <Page />
      </ReactStreaming>
    )
    

Options

  • options.disable?: boolean: Disable streaming.

    The component is still rendered to a stream, but the promise const promise = await renderTo... resolves only after the stream has finished. (This effectively disables streaming from a user perspective, while unlocking React 18 Streaming capabilities such as SSR <Supsense>.)

  • options.seoStrategy?: 'conservative' | 'google-speed'

    • conservative (default): Disable streaming if the HTTP request originates from a bot. (Ensuring the bot always sees the whole HTML.)

    • google-speed: Don't disable streaming for the Google Bot.

      • Pro: Google ranks your website higher because the initial HTTP response is faster. (To be researched.)
      • Con: Google will likely not wait for the whole HTML, and therefore not see it. (To be tested.)
    • Custom SEO strategy: use options.disable. For example:

      // Always stream, even for bots:
      const disable = false
      
      // Disable streaming for bots, except for the Google Bot and some other bot:
      const disable =
        isBot(userAgent) &&
        !['googlebot', 'some-other-bot'].some(n => userAgent.toLowerCase().includes(n))
      
      const stream = await renderToStream(<Page />, { disable })
      
  • options.userAgent?: string: The HTTP User-Agent request header. (Needed for options.seoStrategy.)

  • options.webStream?: boolean: Use Web Streams instead of Node.js Streams in Node.js. (Node.js 18 released Web Streams support.)

Bonus: useAsync()

import { useAsync } from 'react-streaming'

function StarWarsMovies() {
  return (
    <div>
      <p>List of Star Wars movies:</p>
      <Suspense fallback={<p>Loading...</p>}>
        <MovieList />
      </Suspense>
    </div>
  )
}

// This component is isomorphic: it works on both the client-side and server-side.
// The data fetched while SSR is automatically passed and re-used on the client for hydration.
function MovieList() {
  const movies = useAsync(async () => {
    const response = await fetch('https://star-wars.brillout.com/api/films.json')
    return response.json()
  })
  return (
    <ul>
      {movies.forEach((movie) => (
        <li>
          {movie.title} ({movie.release_date})
        </li>
      ))}
    </ul>
  )
}

Get Started (Library Authors)

react-streaming enables you to suspend React rendering and await something to happen. (Usually data fetching.) The novelty here is that it's isomorphic:

  • It works on the client-side, as well as on the server-side (while Serve-Side Rendering).
  • For hydration, data is passed from the server to the client. (So that data isn't loaded twice.)

You have the choice between three methods:

  • useAsync(): Highest-level & easiest.
  • useSsrData(): High-level & easy.
  • injectToStream(): Low-level and highly flexible (both useAsync() and useSsrData() are based on it). Easy & recommended for injecting script and style tags. Complex for data fetching (if possible, use useSsrData() or useAsync() instead).

useAsync()

For how to use useAsync(), see example above.

useSsrData()

import { useSsrData } from 'react-streaming'

function SomeComponent() {
  const key = 'some-unique-key'
  const someAsyncFunc = async function () {
    return 'someData'
  }
  // `useSsrData()` suspends rendering until the promise returned by `someAsyncFunc()` resolves.
  const value = useSsrData(key, someAsyncFunc)
  // `value` is the value returned by `someAsyncFunc()`.
  assert(value === 'someData')
}

If <SomeComponent> is rendered only on the client-side, then useSsrData() is essentially a cache that never invalidates. (If you want to re-run someAsyncFunc(), then change the key.)

If <SomeComponent> is rendered on the server-side (SSR), it injects the resolved value into the stream and the client-side picks up the injected value. (So that the client-side doesn't call someAsyncFunc() but, instead, re-uses the value resolved on the server-side.)

This is for example how useAsync() is implemented:

import { useId } from 'react'
import { useSsrData } from 'react-streaming'

function useAsync(asyncFn) {
  const id = useId()
  return useSsrData(id, asyncFn)
}

injectToStream()

injectToStream(htmlChunk: string) allows you to inject strings to the current stream.

There are two ways to access injectToStream():

  1. With renderToStream():
    import { renderToStream } from 'react-streaming/server'
    const { injectToStream } = await renderToStream(<Page />)
    
  2. With useStream():
    import { useStream } from 'react-streaming'
    
    function SomeComponent() {
      const stream = useStream()
      if (stream === null) {
        // No stream available. This is the case:
        // - On the client-side.
        // - When `option.disable === true`.
        // - When react-streaming is not installed.
      }
      const { injectToStream } = stream
    }
    

Usage examples:

// Inject JavaScript (e.g. for progressive hydration)
injectToStream('<script type="module" src="/main.js"></script>')

// Inject CSS (e.g. for CSS-in-JS)
injectToStream('<styles>.some-component { color: blue }</styles>')

// Pass data to client
injectToStream(`<script type="application/json">${JSON.stringify(someData)}</script>`)

For a full example of using injectToStream(), have a look at useSsrData()'s implementation.

FAQs

Last updated on 23 Apr 2022

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc