React boilerplate bundled with React Router, Material-UI, layouts, and Redux (Redux Thunk implementing Ducks with optional localstorage persistence). Integrates seemlessly with the Firebase platform to enable services such as Firebase Authentication, Cloud Firestore, Cloud Storage and Firebase Hosting. Part of the instant
suite of rapid application development tools and can be used in conjunction with instant-express
if an API/server solution is also required.
Initially created using an ejected version of create-react-app
, the framework has been extended to include the following features out the box:
- Config
- React Router
- Material UI
- Redux
- Redux Thunk
- Redux Localstorage
- Firebase Hosting, Authentication, Cloud Firestore & Cloud Storage
- Code Splitting
- Helmet
- ESLint & Prettier
- Debugging in VSCode
Similar to create-react-app
, you can create an application using instant-tools.
Please note: This will clone the instant-react boilerplate into the specified my-app
directory, and the optional -i
parameter will install all dependencies.
$ npx instant-tools create react my-app -i
$ npm i instant-tools -g
$ instant create react my-app -i
Please note: This will install the instant-tools CLI globally, which will come in useful later on for generating new project files such as components and Redux modules.
It will create a directory called my-app
inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies:
my-app
├── LICENCE
├── README.md
├── node_modules
├── package.json
├── database.json
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── .env
├── .eslintrc
├── .gitignore
├── .prettierrc
├── config
│ ├── default.json
│ ├── development.json
│ ├── env.js
│ ├── paths.js
│ ├── polyfills.js
│ ├── production.json
│ ├── webpack.config.dev.js
│ ├── webpack.config.prod.js
│ └── webpackDevServer.config.js
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
├── scripts
│ ├── build.js
│ ├── firebase.js
│ └── start.js
│ └── test.js
└── src
├── App.js
├── App.test.js
├── index.css
├── index.js
├── registerServiceWorker.js
├── Routes.js
├── Theme.js
├── config
└── theme.js
├── containers
├── About.js
├── Home.js
├── PageNotFound.js
├── Profile.js
├── Settings.js
└── SignIn.js
├── layouts
└── Default.js
└── redux
└── modules
├── auth.js
├── client.js
└── reducer.js
Just like create-react-app
, instant-react
assumes the following.
For the project to build, these files must exist with exact filenames:
public/index.html
is the page template;src/index.js
is the JavaScript entry point.You can delete or rename the other files.
You may create subdirectories inside
src
. For faster rebuilds, only files insidesrc
are processed by Webpack.
You need to put any JS and CSS files insidesrc
, otherwise Webpack won’t see them.Only files inside
public
can be used frompublic/index.html
.
Read instructions below for using assets from JavaScript and HTML.You can, however, create more top-level directories.
They will not be included in the production build so you can use them for things like documentation.
Once the installation is done, you can open your project folder:
cd my-app
Inside the newly created project, you can run some built-in commands:
Associates the project with a Firebase project. You will be provided with a list of Firebase projects to choose from.
Please note: This assumes you have created a Firebase project for this purpose and you've logged into Firebase using the firebase-tools
CLI.
Please note: This is currently a pre-requisite and the application won't load without an associated Firebase project. However, this dependency may be removed in the future.
Runs the app in development mode. Open http://localhost:3000 to view it in the browser.
The page will automatically reload if you make changes to the code. You will see the build errors and lint warnings in the console.
Runs the test watcher in an interactive mode. By default, runs tests related to files changed since the last commit.
Builds the app for production to the build
folder. It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed.
Deploys the build
folder to the associated Firebase project, via Firebase Hosting. Once deployed you'll see the deployed URL in the console.
Builds the app for production and deploys the build
folder to the associated Firebase project. A combination of the npm run build
and firebase deploy
commands.
instant-react
(and in fact most of the instant
suite) relies heavily on node-config. Although the default ejected application generated by create-react-app
utilizes .env
files (you can see how to implement them in this helpful article), instant-react
utilizes node-config
as it can be used for both client and server-side applications with only slightly varying usage. This ensures consistent behaviour across all the instant
tools.
With node-config
we can define a standard set of variables or settings that apply across all environments, and then define a specific set of environment variables that can be used to override the defaults.
In our case the settings that apply across all environments are stored in config/default.json
; local development settings are stored in config/development.json
; and all production settings are stored in config/production.json
.
Please note: Sensitive config values should only be committed to the repository after being encrypted. As a consequence, the development.json
and production.json
files are excluded by git via the .gitignore
file by default. Please see Securing Production Config Files for more information.
In addition to the settings you define in these files, the Firebase project settings are automatically pulled down via firebase-tools
when the npm start
and npm build
commands are executed, and added to the application's config object.
Please see the Modfiying Config section for information on how to add custom settings to your application.
Although not enforced, instant-react
adheres to the concept that all reusable React components are stored in src/components
. This could include atomic elements such as buttons, alerts or progress bars. Components which map to URL routes are stored in src/containers
and should only be referenced once by your application, in the src/Routes.js
file (please see Routing for more information). This could include the Homepage, the About page, the Sign-in page and so on. Although similar to components in technical design, containers allow us to separate application pages from the atomic components they utilize.
See the Adding Components & Containers section for information on how to add new components and containers to your applicaton.
The src/Routes.js
file contains the URL routing configuration and implements React Router and Code Splitting.
Routes are split into three different types:
Authenticated
: Access is only granted if the user is authenticated, e.g Profile page.Unathenticated
: Access is only granted if the user is unauthenticated, e.g About page.Applied
: Access is granted whether the user is authenticated or unauthenticated, e.g. Homepage.
Attempted access to an Authenticated
route by a user that isn't authenticated will result in the user being redirected to /signin
and the attempted page URL will be appended as a redirect
URL parameter (once successfully signed in, the user will be redirected to that page), e.g http://localhost:3000/signin?redirect=profile
.
You'll notice another, special, type of route too:
Core
: If authentication has been enabled then this will act as anAuthenticated
route. If authentication has been disabled then this will act as anUnauthenticated
route. This can be useful if you want hide or show all routes in one go without having to alter theRoutes.js
file. Authentication can be enabled or disabled by toggling theapp.useAuth
setting inconfig
.
Your application can implement any of these routes.
Containers are imported asyncronously so that they are only loaded into memory when the revelant URL route is triggered. This prevents all of our containers being loaded at once unecessarily.
A default 'catch all' route is also specified which displays the PageNotFound
container.
See the Routes section for information on how to add new routes to your applicaton.
Material UI is enabled by default and is utilized by the core instant-react
components and containers. In addition, instant-react
provides template theming, styling properties and layouts.
Material UI utilises the Google Roboto font and Material Font Icons which are referenced in public/index.html
.
See the Themeing, Styling and Layouts sections for information on how to customize the look and feel of your application.
instant-react
has been configured to utilise Redux, Redux Thunk and Redux Localstorage. We've opted to use the Ducks Modular Redux pattern for the core Redux modules, but you don't have to.
Redux is useful for large applications and can be integrated into your application with very little effort. Please see the Using Redux section for information on how to use Redux in your application.
instant-react
has been designed to integrate seemlessly with Firebase. This means it's incredibly easy to utilise Firebase services such as Firebase Authentication, Cloud Firestore, Cloud Storage and Firebase Hosting.
To enable authentication, please ensure the app.useAuth
setting in config
is set to true
.
Next, head to the Firebase console and ensure one of the Sign-in providers has been enabled in the "Sign-in method" section of "Authentication".
Please note: instant-react
currently only supports the Email/Password
provider. As a consequence, you'll need to manually add users to the "Users" section of "Authentication".
Restart the React server (remember, config
settings don't take effect without restarting the server) and navigate to http://localhost:3000/signin.
React Helmet enables us to modify page headers at runtime, for example the title or meta data attributes.
See Using Helmet for more information on how to customize page headers at runtime.
ESLintESLint & Prettier have been configured to ensure consistent code formatting and style. ESLint highlights areas of the code that don't adhere to a coding standard, and Prettier automatically fixes code to match the standard. To enable these to work in VSCode, you'll need to install the ESLint and Prettier extensions, as well as modifying your settings so that files can be updated on save.
"editor.formatOnSave": true
Prettier is also configured to run as a precommit hook so that any staged files are automatically updated before commit.
For more information please see this useful article.
It's recommended that you modify the package.json
file to customise the name
and description
fields for your project (and add a version
field if necessary - Semantic Versioning is strongly recommended). This information is accessed by the application and displayed in the About
page by default (via the /about
URL route).
It's also recommended that you change the proxy
value if you intend to make use of server requests in development mode.
Please see the Fetch Requests section for more information on using an external server for requests.
Assuming your config/default.json
file looks like this
{
"testing": 123
}
It would be possible to access this value from within your React code as follows.
import config from 'config';
console.log(CONFIG.testing);
Please note: If you add new configuration values to these files and you're running Webpack Dev Server (by virtue of running npm start
, then you'll need to restart the server in order to view these settings in your application).
Please note: The CONFIG
object has been added to the globals
option of .eslintrc
to hide warnings regarding the non-declaration of this variable.
Please see the node-config
documentation for more information.
As with any React application, you can make use of the Fetch API
to fetch resources, such as requesting data from an API.
To serve data from an API using the same host as the React application, you can add the server URL to package.json
, so that requests like fetch('/api/todos
) will be proxied to the server.
package.json
"proxy": "http://localhost:5001/[node-project-id]/us-central1/app/"
Please see the create-react-app docs for more information.
To ease the process of making server requests, instant-react
is bundled with a request helper that utilizes the instant-request
package. For this to work you'll need to modify your config to include the URI prefix that all requests will use (by default this is just "/api").
default.json
"api": {
"url": "/api"
}
Now you can import the request
helper into your code from instant-react-core
, and create a new instance of the class.
import Request from 'instant-react-core/lib/utils/request';
const request = new Request();
It's now possible to make POST, GET, PUT and DELETE requests by using your new request instance.
const createTodo = async () => {
try {
const todo = await request.post('/todos', {
id: '1',
description: 'Book holiday',
});
return todo;
} catch (error) {
throw error;
}
};
const getTodos = async () => {
try {
const todos = await request.get('/todos');
return todos;
} catch (error) {
throw error;
}
};
const updateTodo = async () => {
try {
const todo = await request.put('/todos/1', { completed: true });
return todo;
} catch (error) {
throw error;
}
};
const deleteTodo = async () => {
try {
await request.delete('/todos/1');
} catch (error) {
throw error;
}
};
This type of code can be utilized in your Redux modules. Please see the Using Redux section for information on how to use Redux in your application.
@todo
@todo
@todo
@todo
@todo
To use Helmet, simply import the library and wrap your page headers in a <Helmet>
tag.
import React, { Fragment } from 'react';
import { Helmet } from 'react-helmet';
function() {
return (
<Fragment>
<Helmet>
<title>Home</title>
<link rel="canonical" href="http://mysite.com/example" />
</Helmet>
<div>
Your content
</div>
</Fragment>
);
}
The process.env.NODE_ENV
variable is set to development
in package.json
when the npm start
command is executed, and Firebase automatically sets the process.env.NODE_ENV
variable to production
in hosting. The
node-config
doesn't work with Webpack
out of the box because it reads the config files at runtime, which can't be done when the code is running on a browser. Therefore, the Webpack
config file has been amended to enable config values to be read at runtime (see https://github.com/lorenwest/node-config/wiki/Webpack-Usage#option-2 for more information).
Please note: We opted for Option 2 over Option 4 (even though 4 enabled the config get()
method) as Option 4 causes warnings to be displayed. It may be worth reassessing this in future versions of node-config
, Webpack
and react-scripts
.
We'd greatly appreciate any contribution you make.