Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

casterly

lucasecdb82MIT0.8.0TypeScript support: included

CLI for Casterly

readme

Casterly

CI

Isomorphic server rendering library for React apps.

Disclaimer: the API for this library is still experimental and should be considered unstable. For production apps it is advised to use other SSR libraries such as Next.js or Remix.

Installation

To install and start using Casterly, run the following commands

npm i --save-dev casterly
npm i --save react \
  react-dom \
  react-router@next \
  react-router-dom@next \
  @casterly/components \
  @casterly/express

Getting started

To start developing with Casterly, you need to create a few files with the following content:

// server.js
const { createRequestHandler } = require('@casterly/express')
const express = require('express')

const app = express()

app.use(createRequestHandler())

app.listen(3000, () => {
  console.log('Server started on http://localhost:3000')
})

This file is your app entrypoint, you have full control over the server and can add any other express middlewares you'd like or need.

// app-server.jsx
import { Scripts, Styles } from '@casterly/components'
import { RootServer } from '@casterly/components/server'
import React from 'react'
import { renderToNodeStream } from 'react-dom/server'

import App from './src/App'

const Document = () => {
  return (
    <html>
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <Styles />
      </head>
      <body>
        <div id="root">
          <App />
        </div>
        <Scripts />
      </body>
    </html>
  )
}

export default function (request, statusCode, headers, context) {
  const content = renderToNodeStream(
    <RootServer context={context} url={request.url}>
      <Document />
    </RootServer>
  )

  content.unshift('<!doctype html>')

  return new Response(content, {
    status: statusCode,
    headers: {
      ...Object.fromEntries(headers),
      'content-type': 'text/html',
    },
  })
}

This file contains your SSR entrypoint, you can render you HTML however you'd like and place your scripts in either the <head> or <body> section according to your needs. You should always render you root component in this file wrapped in the <RootServer> component, passing in the required props as above, else your app won't work correctly.

// app-browser.jsx
import { RootBrowser } from '@casterly/components/browser'
import React from 'react'
import ReactDOM from 'react-dom'

import App from './src/App'

ReactDOM.hydrate(
  <RootBrowser>
    <App />
  </RootBrowser>,
  document.getElementById('root')
)

This is the same as the above, but for the browser. You can choose to use either ReactDOM.hydrate or the new (unstable) ReactDOM.unstable_createRoot to opt-in React's concurrent mode. Similar to its server counterpart, you should always render your app wrapped in the <RootBrowser> component.

// src/App.jsx
import { Routes } from '@casterly/components'

const App = () => {
  return <Routes />
}

export default App

This is you root component, where you should place any components that is common in every page of your app, such as Redux or Apollo's context providers.

// src/routes.js
export default [
  {
    component: () => import('./index'),
    path: '/',
  },
]

The routes.js file is where you should add your app routes. It follows a similar format to React Router's object-based routes config (because we use React Router under the hood), but instead of adding your component directly into this file, you should pass in a function that returns a Promise that resolves to the module of that route's component. You can use React Router hooks to get routes parameters and to navigate to other routes. For more information, check their docs.

const IndexPage = () => {
  return <h1>Hello world</h1>
}

export default IndexPage

Last but not least, you have your index page component, which is now only rendering the default "hello world" message.

Now, you can start you server by running node server.js and, in another terminal window, running npm run casterly watch. Your app will be running in localhost in the port 3000. The casterly watch command will spin up a build server that will build your app into a bundle that we can serve in the browser, which your app server (the one in server.js) will use during development and production to render the app.

License

MIT License © 2020 Lucas Cordeiro

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Changed

  • Removed peer dependency react-hot-loader.
  • Upgrade workbox-webpack-plugin version to v5 stable.

Fixed

  • Static assets not served with correct mime-type in production.
  • Error page without noindex, nofollow robots rules.

[0.1.6] - 2019-09-28

Changed

  • Replaced the usage of webpack-node-externals with a custom externals function to avoid duplicate React copies over several modules.

[0.1.5] - 2019-09-27

Fixed

  • Add @apollo/* packages to externals whitelist, to avoid different React's from being imported.

[0.1.4] - 2019-09-27

Added

  • Connect to chrome devtools inspector using the inspector node package.

Changed

  • Update proxy middleware to use config from the app's package.json.

Removed

  • Custom config added in template response, it should be configured in userland.

[0.1.3] - 2019-09-27

Fixed

  • Error when running rs build of invalid call to map.

[0.1.2] - 2019-09-27

Fixed

  • Sass imports not working (File to import not found or unreadable).
  • Error when statically linking the package (Error: Cannot find module 'react-hot-loader/babel').

[0.1.1] - 2019-09-27

Fixed

  • Error in build script with missing bfj package.

[0.1.0] - 2019-09-27

Added

  • Initial version of package with rs cli.