A universal Javascript starter kit inc. React, Redux, Redux Dev Tools, Universal Redux Router, CSS Modules, hot module reloading, Babel for ES2015+ and ESLint

Overview

UniversalJS

A universal Javascript starter kit inc. React, Redux, Redux Dev Tools, Universal Redux Router, CSS Modules, hot module reloading, Babel for ES2015+ and ESLint.


Test status Dependencies status

Demonstrates

Read more about the reasoning behind these decisions.

Although this setup is far from simple or minimal, I have purposefully avoided all but what I see as core for a large project. For example, authentication and data fetching is outside the scope of this starter kit.

Usage

mkdir new-project; cd new-project
git clone [email protected]:colinmeinke/universal-js.git .
npm install
npm run build:dev
npm run start:dev

Structure

Entry points

The /src directory is where all of the code for the app lives.

The build process npm run build:dev or npm run build:pro will compile this code and output it into the /dist directory.

Files that will be directly requested by the browser are then copied into their appropriate directory within /static.

There are two files in the root of the /src directory:

  1. server.js
  2. client.js

These two files are the entry points into the app for their respective environments.

server.js will listen for HTTP requests sent from a client to the server. On receiving a request it will render the response on the server and send it back to the client to display.

client.js will kick in if Javascript is enabled and initialises in the browser. This will hydrate the app on the client, and thereafter handle requests itself, removing the need for additional requests to the server.

Shared code

The /src/common directory is where all of the shared code lives for both server and client. This is the beauty of universal Javascript – shared code.

Within are three directories for Redux actions, reducers and store configuration/actions, /reducers, /store.

Components

The /components directory also resides within the /common directory. This is where all React components live. Both presentational and container (those that connect to the Redux store or have access to its dispatch method).

Each presentational component lives within its own directory within /components. The directory is named after the component in question, e.g. <Button /> would be: /components/Button/....

Connecting components to the Redux store is done by adding a file to the root of the /components directory. This file is also named after the component, e.g. /components/Button.js.

This structure means that all components can be imported into any file as follows:

import Button from './components/Button';

The file importing the component does not need to know if it is a presentational or a container component.

This is because of how imports resolve. It will first look for a file within the /components directory called Button.js. If that does not exist, it will then look for the index.js file within a directory called /Button.

|-- common
    |-- components
        |-- Button
            |-- index.js
            |-- base.css
        |-- Button.js

Terms, concepts and reasoning

Progressive enhancement

I get worried when I see very complex things getting built, things that are reliant of JavaScript. While its true that very few people are going to turn off JavaScript, the JavaScript can still fail. That can be okay if you're building in the right way. It will fall back to what's underneath the JavaScript.

- Jeremy Keith

Server-side rendering

If progressive enhancement is an aim, then we must provide the core experience of our app to users who have disabled Javascript in their browser, or situations where client-side Javascript has failed.

This necessitates that we render the same app on the server as we might on the client. The response from the server should be usable regardless of whether the client-side Javascript kicks in. That's just a bonus!

Server-side rendering isn't just about progressive enhancement and accessibility. It's also a huge win for performance and SEO.

Universal Javascript

If we are rendering the same experience on both the server and the client, then it follows that we should use the same language to build both.

When we use the same language for everything, it means we can abstract common code and share it between environments. Huge wins for maintainability, testing, cognitive load ... the list goes on and on.

NodeJS

NodeJS makes universal Javascript possible by running Javascript on the server.

Express

Express runs in a NodeJS environment and makes it easy to handle HTTP requests to the server and send a response.

React

At its core, this is a React app. React is how we write our components, render the user interface and keep it in sync with the state of the app.

React also makes rendering on the server really easy.

Flux

We need a way to manage the state of our React components. Flux is a pattern that can be used to architect how state flows through our app and how we update state.

However, Flux itself is only an idea. You can't download or install it.

Redux

Redux is a Flux implementation. It is a library that you can download or install.

It's beautifully simple and stores all app state in a single object.

It allows us to treat our user interface as a pure function, which when passed identical state will always render identical output.

It also allows our state to be serializable, and therefore storable or shareable. This makes the possibility of things like undo, redo, debugging and cross-device syncing very achievable.

Universal Redux Router

Universal Redux Router is a router I built to turn URL params into first-class Redux state. For full documentation head over to that repository.

CSS Modules

As much as I love working with inline styles, and using the power of Javascript to output styles, there is a lot to be said for CSS Modules.

CSS Modules allow you to write CSS that is locally scoped. This means that a CSS file can use the same class names that are in another CSS file without worrying about clashing. When the CSS is output, each class gets a unique hash – no need to rely on long-winded naming conventions like BEM.

Most importantly for me is that you can extract the CSS written this way into an external style sheet. This means you still get styling even if Javascript fails on the client.

A downside for me was getting CSS Modules setup in the first place to work how I wanted. For more comprehensive documentation on how to setup CSS modules with Webpack, check out another repository of mine that describes exactly that.

PostCSS

PostCSS is a preprocesser, a bit like Sass or Less. The difference is it works on a plugin system.

With the right plugins installed PostCSS allows you to write future CSS, and compile it to something that works on today's browsers. This is its major strength for me – you can just write CSS.

CSS themes

If we write all user interface as components, all of our CSS can be split by component too.

I like to allow theming, giving each presentational component a base.css file and then overriding those styles with a ${theme-name}-theme.css file.

base.css typically contains base layout rules and a very simple grayscale color palette.

ES2015+

The Javascript features and syntax used within this repository follows the ES2015 spec.

Babel

The reason we don't have to care about browser support for the Javascript features and syntax we write, is because Babel takes care of transpiling our code to ES5. ES5 works on all modern browsers.

Build process

All build tasks are run using the surprisingly powerful scripts property built into npm.

Everything that can be done on the command line, can be done with scripts.

Here's a list of the some of the scripts I have setup:

  • npm run build:dev – build to run in a development environment.
  • npm run build:pro – build to run in a production environment.
  • npm run changelog – create or update a changelog.
  • npm run commit – create a conventional commit message.
  • npm run lint – lint the code.
  • npm run start:dev – start the server in a development environment.
  • npm run start:pro – start the server in a production environment.
  • npm run test – run the tests.

Webpack

Webpack is the powerhouse behind the build scripts npm run build:dev and npm run build:pro.

Webpack takes a Javascript file as an entry point. It runs through that file's dependencies, and its dependents' dependencies, bundling all that code into an output file.

In your Webpack config, you can tell Webpack to run various loaders on specific file types during bundle-time. For example, in this case we run all our Javascript files through the Babel loader to convert our ES2015+ features and syntax to regular ES5.

Loaders can be chained together, which can be very powerful.

For more information, check out the section on entry points above.

Hot module reloading

Part of a great development environment is not having to manually recompile your code and refresh your browser every time you make a change to your Javascript or CSS.

Hot module reloading solves this.

ESLint

Maintaining a consistent coding style is important, especially when there is more than one contributor.

npm run lint will run ESLint on the Javascript using standard style.

Commitizen

Commitizen helps us write conventional commit messages. When commiting code, instead of git commit -m "..." type npm run commit.

This guides us through the process of writing a conventional commit message by prompting us for various data about the changes we have made.

As well as maintaining consistent commit messages across the project, this can have other extremely useful benefits.

There are libraries such as sematic release or conventional changelog that can understand the conventional commit message syntax and run tasks based on that.

Help make this better

Issues and pull requests gratefully received!

I'm also on twitter @colinmeinke.

Thanks 🌟

License

ISC.

Comments
  • How the css is integrated in the boilerplate? (question)

    How the css is integrated in the boilerplate? (question)

    What this code does?

          {
            exclude: /\/Page/,
            loader: 'css-loader?modules',
            test: /\.css$/,
          },
          {
            include: /\/Page/,
            loader: ExtractTextWebpackPlugin.extract(
              'style',
              'css?modules&importLoaders=1!postcss'
            ),
            test: /\.css$/,
          },
    

    I can't understand it. Looks like the Page component styles are moved into a .css file? I'm having problems with the CSS because my css at components like

    is ignored :/

    opened by felixsanz 6
  • onChangeWithDebounce not working properly in my local build

    onChangeWithDebounce not working properly in my local build

    I built and started the repo on my pc, everything works fine, except for the name edit, it isn't firing the UPDATE_NAME action. I found out it caused by the e.target.value closure into the onChangeWithDebounce function ( ./src/common/components/input/index.js ), the console in the browser actually give me this error:

    Uncaught TypeError: Cannot read property 'value' of null

    I was able to solve the problem modifying the function:

    const onChangeWithDebounce = e => {
        clearTimeout( debounce );
        const inputValue = e.target.value;
        debounce = setTimeout(() => {
          onChange( inputValue );
        }, 500 );
      };
    

    I don't know why, but it works now.

    bug 
    opened by cluk3 6
  • Router or entire boilerplate doesn't work with Koa

    Router or entire boilerplate doesn't work with Koa

    I've wasted 12 hours today on this, and finally come to the conclusion that the minimal integration will suffer from the same problem :sob:

    If you replace express with koa, you get: [Error: Can't set headers after they are sent.]

    The problem occurs here:

    app.use(async ctx => {
      configureStore({ isServer: true, url: ctx.url }).then(store => {
        ctx.type = 'text/html; charset=utf-8' // <-- here
    

    I can't set ctx.type (needed since renderToStaticMarkup is a octet-stream) because your boilerplate is sending stuff before my headers.

    More exacly getState( url, routes, reducer ).then( state => { in configureStore.js is the thing that causes this.

    To make this more debuggable, this express app works:

    const app = express()
    
    app.use(({ url }, res ) => {
      configureStore({ isServer: true, url }).then( store => {
        res.write( '<!DOCTYPE html>' );
    
        renderToStaticMarkup(
          <Page
            app={ renderToString( <Root store={ store } /> )}
            scripts={ scripts }
            styles={ styles }
            title={ DocumentTitle.rewind() }
          />
        ).pipe( res );
      }).catch( console.error.bind( console ));
    });
    

    and this koa app gives "not found" in browser, probably because the error [Error: Can't set headers after they are sent.] stops the koa app.

    const app = new Koa()
    
    app.use(async ctx => {
        configureStore({ isServer: true, url: ctx.url }).then(store => {
          ctx.type = 'text/html; charset=utf-8'
          ctx.body = 'asdsds' // <-- doesn't work anyway
        }).catch(console.error.bind(console)) // eslint-disable-line no-console
    })
    

    Can you help please? The router must be doing something fancy that sends data before my header is set

    opened by felixsanz 3
  • React file size

    React file size

    Why /static/js/react.min.js is 136kb ? I tried to figure it out but i failed!

    Also client.min.js is 45.5Kb when the apps is almost a hello world!

    Thanks for the boilerplate btw :)

    I closed the issue because the answer is: gzip

    opened by felixsanz 2
  • Selectors for this proyect?

    Selectors for this proyect?

    I know you use this boilerplate and it's more like a proyect to learn react/redux and become more confident about it; so, have you take a look at https://github.com/reactjs/reselect ? do you think it will fit here?

    opened by felixsanz 1
  • question about code never reached

    question about code never reached

    this never triggers:

          if (__DEVELOPMENT__ && module.hot) { // eslint-disable-line no-undef
            module.hot.accept('../reducers', () => {
              console.log('this never appears, neighter in node nor in the browser')
              const nextEnhancer = routerReducer(require('../reducers'))
              store.replaceReducer(nextEnhancer)
            })
          }
    

    any idea?

    opened by felixsanz 1
  • routing empty module.hot.accept

    routing empty module.hot.accept

    in routes.js, you can see this code:

    let routes = getRoutes()
    
    if (__DEVELOPMENT__ && module.hot) { // eslint-disable-line no-undef
      module.hot.accept()
      routes = getRoutes()
    }
    
    1. routes contain the same, it's like a duplication, right?
    2. why module.hot.accept() is empty?

    Sorry if asking too much, but this is by far the best boilerplate i've seen (and trust me, i've seen so many), and i would like to understand it 100%, master it, and also being able to contribute with code

    opened by felixsanz 1
  • Feature request: Add relay/graphql

    Feature request: Add relay/graphql

    Relay is already server-side ready, and provides a way to fetch data that components need, automatically, defining the fragments of data in each component. (As you probably know xD)

    Here is a repo example of server-side relay: https://github.com/denvned/isomorphic-relay

    Would you consider adding relay/graphql to the boilerplate? It would be awesome!

    opened by felixsanz 1
  • transform-object-rest-spread plugin what for?

    transform-object-rest-spread plugin what for?

    What is transform-object-rest-spread plugin used for?

    The plugin does "Compile object rest and spread to ES5", but i don't know why is this useful?

    Most browsers support it, whats the use case here?

    I'm loving your starter kit, been struggling this boilerplates for a month and yours is brilliant

    opened by felixsanz 1
  • Missing static/images/favicon.ico file prevents from starting the server

    Missing static/images/favicon.ico file prevents from starting the server

    Hi,

    First, I really like your starter kit!

    When trying to run the server I got this error that prevents from starting the server:

    (local (master)~/Codes/universal-js)$ npm start --development
    
    > [email protected] start /Users/me/Codes/universal-js
    > node ./dist/server.js
    
    Error: ENOENT: no such file or directory, stat '/Users/me/Codes/universal-js/static/images/favicon.ico'
        at Error (native)
        at Object.fs.statSync (fs.js:849:18)
        at favicon (/Users/me/Codes/universal-js/node_modules/serve-favicon/index.js:64:15)
        at Object.<anonymous> (/Users/me/Codes/universal-js/dist/server.js:98:40)
        at Object.<anonymous> (/Users/me/Codes/universal-js/dist/server.js:132:31)
        at __webpack_require__ (/Users/me/Codes/universal-js/dist/server.js:21:30)
        at obj.__esModule.default (/Users/me/Codes/universal-js/dist/server.js:41:18)
        at Object.<anonymous> (/Users/me/Codes/universal-js/dist/server.js:44:10)
        at Module._compile (module.js:434:26)
        at Object.Module._extensions..js (module.js:452:10)
    

    Doing a touch static/images/favicon.ico fixed the issue but if you got a real favicon that would probably be better.

    bug 
    opened by happypoulp 1
  • Support media queries, `:hover` states, vendor prefixing etc.

    Support media queries, `:hover` states, vendor prefixing etc.

    Radium is a potential solution, but is not suitable for server side rendering. Plus adding event listeners for :hover etc. seems less than ideal. Will almost certainly need to move away from inline styles.

    enhancement 
    opened by colinmeinke 1
Owner
Colin Meinke
Full-stack developer.
Colin Meinke
A minimal, fast monorepo template for TypeScript (and React) projects with hot module reloading

A minimal, fast monorepo template for TypeScript (and React) projects with hot module reloading

Jordan Sexton 24 Nov 4, 2022
Babel React Koa Hot Universal Boilerplate

Babel React Koa - Hot Universal Boilerplate Breko Hub Breko hub is a github repository that helps anyone create new JavaScript applications. Giving yo

Thomas 143 Nov 4, 2022
A magical boilerplate with hot reloading and awesome error handling™

redux-undo-boilerplate a magical boilerplate with hot reloading and awesome error handling™ that uses webpack, redux, react and redux-undo Installatio

Daniel Bugl 120 Oct 18, 2022
Starter template for Vite with React (TypeScript). Supports Tailwind with CSS-Modules. Jest and @react/testing-library configured and ready to go. Also ESLint, Prettier, Husky, Commit-lint and Atomic Design for components.

Mocking up web app with Vital(speed) Live Demo Features ⚡️ React 17 ?? TypeScript, of course ?? Jest - unitary testing made easy ?? Tailwind with JIT

Josep Vidal 137 Nov 29, 2022
React + React Router 1.0 + Redux + Webpack & Hot Module Replacement

React scaffolding An opinionated scaffolding with the following technical stack and configuration: React (15.x.x) React Router (2.x) Flux by using Red

Rafael 29 Feb 26, 2021
:point_up::running: Modern Relay Starter Kit - Integrated with Relay, GraphQL, Express, ES6/ES7, JSX, Webpack, Babel, Material Design Lite, and PostCSS

Relay Fullstack is a Relay scaffolding application that aims to help you get up and running a project without worrying about integrating tools. It com

Varayut Lerdkanlayanawat 992 Nov 5, 2022
Gab 3 Mar 5, 2022
This project is a React Starter Kit with basic tools and examples of their use.

Welcome React Starter Kit! This project is a React Starter Kit with basic tools and examples of their use. Tools used React: V17 react-hook-form: V7 r

Harold Velez Castaño 1 Oct 27, 2021
React starter kit for static apps (NO SSR): React + Vite + wouter + jest + testing-library + Prettier + ESLint + Stylint

Intention is to have some light-weight starter kit for any react (static) applications. Faster ?? build time, type-safe, linting, etc, are the things which have been considered for this setup.

Manish Keer 4 Jul 26, 2022
A template repo for creating react-ts apps based on vite. Libs like axios, antd, @apollo/client, eslint, stylelint, react-router-dom and @syy11cn/config-router are pre-installed.

Template Repo for React + Typescript + Vite Introduction This is a template repo for projects built with react and typescript on the basis of vite. Fe

Yiyang Sun 11 May 24, 2022
Ts-next-chakra-motion-kit - Starter kit with Next.js, Chakra-UI, Framer-Motion in Typescript

Typescript Next.js Chakra-UI Framer-Motion Starter Kit Start with a powerful tem

Alexandre Gossard 39 Oct 14, 2022
Rollup + React + Babel + Prettier + Strict ESlint and Stylelint + Sass + VSCode + Playground app - Enterprise grade boilerplate

React package boilerplate by HackingBay Rollup + React 17 + Babel + Prettier + Strict ESlint and Stylelint + Sass + VSCode + Playground app - Enterpri

HackingBay 2 Jan 19, 2022
A bare-bones vite + react + typescript starter template with eslint + prettier, vitest + @testing-library and react-router

A bare-bones vite + react + typescript starter template with eslint + prettier, vitest + @testing-library and react-router

Coding Garden 59 Dec 3, 2022
Tail-kit is a free and open source components and templates kit fully coded with Tailwind css 2.0.

Tail-Kit A beautiful and large components and templates kit for TailwindCSS 2.0 Tail-Kit is Free and Open Source. It does not change or add any CSS to

null 2.5k Dec 3, 2022
🧐 A sensible universal starter kit for React + Redux

Universal React Redux Boilerplate A universal React/Redux boilerplate with sensible defaults. Out of the box, this boilerplate comes with: Server-side

null 111 Nov 24, 2022
Universal React Starter Kit ft. Redux Saga

React Universal Saga Getting Started Universal React App Starter featuring Redux Saga. Heavily modified version of React Redux Universal Hot Example c

Kawi Xiao 185 Dec 23, 2021
A hapi React Starter kit with react-router, redux, react-transform

hapi-react-starter-kit A hapi React Starter kit with react-router, redux, react-transform Light and fast - Don't be sad, be hapi! Inspired by This rep

Roberto Ortis 171 Oct 24, 2022
React + Redux + Firebase + Webpack + React Hot Loader 3 + React Router in one boilerplate

Firebase 3.0 Starter using React Redux This is a Firebase 3.0 start using React and Redux. It uses the latest version of libraries, including the bran

Douglas Correa 377 Nov 29, 2022
My best-practices-included universal frontend starter kit

Redux Starter Kit Web application starter kit. React Optimizations PWA-optimized, server-side rendering, offline-first with Service Workers, prefetch

Oleg Akbarov 1k Nov 29, 2022