A boilerplate for creating a SPA in React
React SPA (Full Server)
A boilerplate for creating a SPA in React, with a full server.
Features
- Uses
express
to serve bundled assets. - Utilizes
glamor
for styling. - Favicon updates on bundle creation to ensure a stale favicon doesn't get stuck
in the user's cache. - Uses
cross-env
&cross-conf-env
for multi-platform support. - Uses
concurrently
to run multiple processes needed to start the server in
dev mode, and allows for custom labels for each process to make parsing the
output for the dev easier. - After all's said and done, the whole production app compiles down to a lean
~100kb
(gzipped).
Start up dev server
yarn start:server:dev
When the server starts in dev
mode you can debug server code by visiting
chrome://inspect
(if you're using Chrome as your browser). Then go to the
Sources
tab and find the file you want to debug.
Build and start up the production package
# builds the deployment package & starts a simple server to verify built code
yarn start:server
Demonstrates:
Babel
- How to compile ES6 code that's utilizing Webpack aliasing and imports, down to
CommonJS that the server can utilize. Just compiling over to CommonJS, and not
a bundle, is useful for debugging and inline manipulation. - How to integrate Webpack's aliasing during transpilation.
Express
- How to set up logging for routes, so you're aware of what routes are actually
processing the page. - How to use
nodemon
andreload
while in dev to get automatic server and
page reloads when files change.
General JS Stuff
- How to set up a
logger
that can log the same colored output on the server
as it does the client.
Jest
- How to reuse Webpack aliasing for easier file resolution.
React
- How to set up SSR and client data/style hydration.
- How to set up infinite scrolling of items using react-waypoint
and Redux. - How to set up custom view transitions without the use of
react-transition-group
(since it doesn't support that out of the box). By that I mean you can have
default view transitions for most pages, or custom transitions based on the
route you're comingfrom
andto
, or visa versa. - How to set up a per-view theming mechanism.
- How to set up and utilize
breakpoints
with components and their styles. - How to set up async data loading so components render on the server or show
a spinner on the client.
Webpack
- How to use the
DefinePlugin
to:- Expose (non-sensitive) file-system data on the client with
window.WP_GLOBALS
. - Set up requires/imports so that server specific code is stripped out during
compilation so you don't get errors on the client, and smaller bundles. All
from the use ofprocess.env.IS_CLIENT
.
- Expose (non-sensitive) file-system data on the client with
- How to set up aliasing so that imports are clean and don't contain any
../../../../../
craziness. Also useful during refactors when folders
get moved around, you just have to update the paths inconf.app.js
and
you're all set. - How to set up bundle filename hashing correctly so that they only change
when the file contents have changed (allowing the user to keep old bundles
in cache).
How Does It All Work?
Files of note:
.
├── /dist
│ ├── /private # ES Webpack bundles (exposed to the users)
│ └── /public # CommonJS compiled server code (nothing in here should be exposed to users)
│
├── /src
│ ├── /components # Where all the React components live
│ │ ├── /Main # Where are all the React routes are set up
│ │ ├── /Shell # Uses a BrowserRouter or StaticRouter based on the env it's running on
│ │ ├── /ViewHOC # Ensures data is loaded before the view is rendered
│ │ ├── /ViewLoader # Displays the spinner and maintains scroll position
│ │ ├── /views # Where all the views live (think of them like pages)
│ │ └── /ViewTransition # Handles transitioning between pages
│ │
│ ├── /server # All server specific code
│ │ ├── /routes # Separate files for each route handler
│ │ │ ├── catchAll.js # The default route handler
│ │ │ └── index.js # Where you combine all your routes into something the server loads
│ │ │
│ │ ├── /views # Should only be one view, but you can house any others here
│ │ │ └── AppShell.js # The template that scafolds the html, body, scripts, & css
│ │ │
│ │ └── index.js # The heart of the beast
│ │
│ ├── /state # Where the app state lives
│ ├── /static # Static assets that'll just be copied over to public
│ ├── /utils # Individual utility files that export one function and do only one thing well
│ ├── data.js # Where the app gets it's data from (aside from API calls)
│ └── index.js # The Webpack entry point for the app
│
└── conf.app.js # The configuration for the app
- Currently there's a
catchAll.js
route inroutes
that then renders the
AppShell
which controls the document HTML, CSS, and JS. - Each
View
that's defined indata.js
is responsible for loading it's own
data
. It does that by providing a static value, or via a function that
returns a Promise. ViewHOC
will display a spinner if it's data isn't found in cache, otherwise
it'll pass the data on to theView
it was provided.- The
Main
component handles the SPA routing. So if you need to add
routes that aren't defined innavItems
forheader
orfooter
(withindata.js
), you need to add them tootherRoutes
(withindata.js
). - Everything under
src
will be compiled in some way. Parts ofsrc
will be
bundled and dumped indist/public
and everything will be transpiled to
dist/private
so that the server code can 1 - be debugged easily (not being
bundled) and 2 - make use of imports (so no mental hoops of "should I use
require or import"). - Not using
webpack-dev-middleware
because it obfuscates where the final
output will be until a production bundle is created, and you have to add extra
Webpack specific code to your server. With the use of theTidyPlugin
,
reload
, andwebpack-assets-manifest
in conjunction with thewatch
option - we get a live-reload representation of what the production server
will run.
Notes about the dev
server:
- Sometimes running
rs
while the server is in dev mode, will exit withCannot read property 'write' of null
. This will leave a bunch of zombie node
processes, just runpkill node
to clean those up. - Sometimes after killing the server, you'll see a bunch of
node
processes
still hanging around in Activity Monitor or Task Manager, but if you wait a
couple seconds they clean themselves up.