Yeoman generator for starting projects using React and Firebase (Redux optional)
Install Yeoman and generator-react-firebase using npm (we assume you have pre-installed node.js):
npm install -g yo generator-react-firebase
engines
setting in functions/package.json
which defines the function runtime node version (more in the FAQ section)firebase-tools
installed an you are logged in (firebase login
)mkdir myProject && cd myProject
yo react-firebase
(project will be named after current folder)Start application: npm start
or yarn start
Project will default to being named with the name of the folder that it is generated within (in this case myProject
)
Note: To skip installing dependencies during generation pass the skip install flag (i.e. --skip-install
)
.env.local
. This was generated for you for your local development environment, but is is ignored from git tracking (within .gitignore
). You can have different settings within this file based on environment or if multiple developers are running the same code.^17.0.0
(has hooks)React.lazy
and React.Suspense
)prettier
)When opting out of redux (default)
When opting into redux
Sub generators are included to help speed up the application building process. You can run a sub-generator by calling yo react-firebase:<name of sub-generator> <param1>
.
Example: To call the component
sub-generator with "SomeThing" as the first parameter write: yo react-firebase:component SomeThing
Another argument can be passed to sub generators (unless otherwise noted) to provide the base path in which you would like to run the generator (starts from src
). For example: yo react-firebase:component Car routes/Project
runs the component generator within the Project route folder meaning that the component will be added to routes/Project/components
instead of the top level src/components
folder.
Generates a Cloud Function allowing the user to specify trigger type (from HTTPS, Firestore, RTDB, Auth, or Storage)
A component is best for things that will be reused in multiple places. Our example
command
yo react-firebase:function uppercaser
result
/functions
--/uppercaser
----uppercaser.spec.js
----index.js
/functions/uppercaser/index.js:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import { to } from "utils/async";
const eventName = "uppercaser";
/**
* @param {functions.Change} change - Function change interface containing state objects
* @param {functions.EventContext} context - Function event context
* @return {Promise}
*/
async function uppercaserEvent(change, context) {
const { params, auth, timestamp } = context;
const { before, after } = change;
console.log("<%= camelName %> <%= eventType %> event:", {
before: before.val(),
after: after.val(),
uid: auth?.uid,
params,
timestamp,
});
// End function execution by returning
return null;
}
/**
* Event handler that fires every time data is updated in Firebase Realtime Database.
*
* Trigger: `RTDB - onUpdate - '/uppercaser/{pushId}'`
* @name uppercaser
* @type {functions.CloudFunction}
* @public
*/
export default functions.database
.ref(`/${eventName}/{pushId}`)
.onUpdate(uppercaserEvent);
Note: This sub-generator does not support the Path Argument (functions are automatically placed within the folder indicated within functions.source
of firebase.json
, otherwise functions
).
Generates a React component along with an option matching style file (either Javascript or SCSS) and places it within /components
.
A component is best for things that will be reused in multiple places. Our example
command
yo react-firebase:component Car
result
/app
--/components
----/Car
------index.js
------Car.enhancer.js // optional
------Car.styles.js // optional (Localized MUI Styling)
------Car.js
------Car.scss // option (SCSS File)
/app/components/Car.js:
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import styles from "./Car.styles";
const useStyles = makeStyles(styles);
function Car({ car }) {
const classes = useStyles();
return (
<div className={classes.container}>
<span>Car Component</span>
<pre>{JSON.stringify(car, null, 2)}</pre>
</div>
);
}
export default Car;
NOTE: Option to use Javascript file for styles is only offered if @material-ui/core
is included in package.json
Generates a Redux Form wrapped React component along with a matching scss file and places it within /components
.
command
yo react-firebase:form Car
or
yo react-firebase:form CarForm
result
/app
--/components
----/CarForm
------index.js
------CarForm.enhancer.js
------CarForm.js
------CarForm.styles.js
/app/components/CarForm.js:
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/core/styles";
import styles from "./CarForm.styles";
const useStyles = makeStyles(styles);
function CarForm({ onSubmit }) {
const classes = useStyles();
const {
register,
handleSubmit,
errors,
formState: { isSubmitting, isValid },
} = useForm({
mode: "onChange",
nativeValidation: false,
});
return (
<form className={classes.root} onSubmit={handleSubmit(onSubmit)}>
<TextField
error={!!errors.name}
helperText={errors.name && "Name is required"}
name="name"
label="Name"
inputRef={register({
required: true,
})}
margin="normal"
fullWidth
/>
<Button
type="submit"
color="primary"
className={classes.submit}
disabled={isSubmitting || !isValid}
>
{isSubmitting ? "Saving..." : "Save"}
</Button>
</form>
);
}
CarForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
};
export default CarForm;
Generates a custom react hook for a react component. The hook is in it's own file
command
yo react-firebase:hook ProjectTile
result
/app
--/components
----/Project
------index.js
------Project.hook.js
With a file that looks like:
export default function useProjectTile() {
// Hook logic
}
Generates a React component along with a matching component (which has an scss file, an enhancer, and its own index file).
command
yo react-firebase:route Project
result
/app
--/routes
----/Project
------index.js
------components
--------ProjectPage
----------index.js
----------Project.js
----------Project.styles.js
Generates a React component along with a matching component (which has an scss file, an enhancer, and its own index file).
command
yo react-firebase:module notification
result
/app
--/modules
----/notification
------components
------actions.js
------actionTypes.js
------index.js
------reducer.js
Note: This sub-generator does not support the Path Argument (functions are already placed within a folder matching their name).
Generates an enhancer for a react component. Also includes an index file that wraps the component in the enhancer.
command
yo react-firebase:enhancer Project
result
/app
--/components
----/Project
------index.js
Project outputted from generator has a README explaining the full structure and details specific to settings you choose. This includes everything from running your code to deploying it. Some of the key pieces of that information are included below:
To add a unit test, create a .spec.js
or .test.js
file anywhere inside of src
. Jest will automatically find these files and generate snapshots to the __snapshots
folder.
Cypress is used to write and run UI tests which live in the cypress
folder. The following npm scripts can be used to run tests:
* Run using Cypress run: `npm run test:ui`
* Open Test Runner UI (`cypress open`): `npm run test:ui:open`
Build code before deployment by running npm run build
. There are multiple options below for types of deployment, if you are unsure, checkout the Firebase section.
npm i -g firebase-tools
Note: Config for this is located within
firebase-ci
has been added to simplify the CI deployment process. All that is required is providing authentication with Firebase:
firebase login:ci
to generate an authentication token (will be used to give Travis-CI rights to deploy on your behalf)FIREBASE_TOKEN
environment variable within Travis-CI environmentIf you would like to deploy to different Firebase instances for different branches (i.e. prod
), change ci
settings within .firebaserc
.
For more options on CI settings checkout the firebase-ci docs
firebase login
firebase init
then answer:
database.rules.json
build
Yes
npm run build
firebase serve
firebase deploy
NOTE: You can use firebase serve
to test how your application will work when deployed to Firebase, but make sure you run npm run build
first.
Complete examples of generator output available in Examples
redux
and Firebase Real Time Databaseredux
and FirestoreFor full projects built out using this as a starting place, check the next section.
open an issue or reach out over gitter if you would like your project to be included
.jsx
extension for React component files? What if I want to use .js
instead?.jsx
is used to clearly identify files which are using React JSX, which is non-standard javascript functionality. Some eslint configurations, such as Airbnb, have this as a rule (here is an issue that discusses why).
If you would still like to use .js
instead, you can switch the extension of all .jsx
files to .js
using the following command:
for f in src/**/*.jsx; do
mv -- "$f" "${f%.jsx}.js"
done
12
instead of a newer version?It is the newest node supported by the Cloud Functions runtime, which is why that is what is used for the suggested build version as well as the version used when building within CI.
process.env.REACT_APP_FIREBASE_projectId
instead of process.env.REACT_APP_FIREBASE_PROJECT_ID
)In CI the settings for you app are loaded dynamically through firebase-tools
when using the apps:sdkconfig
command, the values that are returned use camel-casing. Instead of making things more unclear by changing the case of these variables, they are left matching what the Firebase JS SDK is expecting. This pattern is also used in the .env.local
file.
How do I deploy my application?
The README of your generated project specifies deployment instructions based on your choices while generating. For an example, checkout any of the README.md
files at the root of projects in the examples folder including this one.
How do I add a route?
yo react-firebase:route MyRoute
path
of the new route to constants/paths
(i.e. MYROUTE_PATH
)src/routes/index.js
Where are the settings for changing how my project deploys through Continuous integration?
Within .firebaserc
under the ci
section. These settings are loaded by firebase-ci
Why are there __snapshots__
folders everywhere when opting into Jest?
Jest just recently added support for adding your own snapshot resolver that allows you to place the __snapshots__
folder at the top level (logic included in scripts/snapshotResolver.js
). Since feature is still in alpha, it is not yet included with this generator. While testing supporting a top level __snapshots__
folder, there were a number of issues, but the provided resolver did work as expected in some cases.
I got it working by:
yarn eject
)24.0.0-alpha.6
- yarn add jest@beta --dev
scripts/snapshotResolver.js
package.json
(which should contain jest config after ejecting):
"snapshotResolver": "<rootDir>/scripts/snapshotResolver.js"
How do I move/rename the cypress
folder to something more general?
If you wanted to move the cypress
folder into test/ui
for instance, you could modify your cypress.json
file to match the following:
cypress.json
{
"chromeWebSecurity": false,
"fixturesFolder": "test/ui/fixtures",
"integrationFolder": "test/ui/integration",
"pluginsFile": "test/ui/plugins/index.js",
"screenshotsFolder": "test/ui/screenshots",
"videosFolder": "test/ui/videos",
"supportFile": "test/ui/support/index.js"
}
Some of my answers were saved, how did that happen? Why?
Yeoman has the store
option, which uses the Yeoman Storage API to store answers to questions within within a .yo-rc.json
. This allows you to rerun the generator in the future to recieve updates without having to remember the answers you used or re-lookup them up.
This also shows you how examples were done by answering the generator questions.
How can I extend the build settings of my app after generating?
There are two options:
npm run eject
or yarn eject
to eject from react-scripts (this cannot be undone)customize-cra
and react-app-rewired
to modify settings without ejecting (for more see the question about not ejecting)How do I extend the webpack/babel config without ejecting?
Install customize-cra
and react-app-rewired
:
npm i --save-dev customize-cra react-app-rewired
Add the following to the scripts
section of your package.json
file:
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"eject": "react-app-rewired eject",
"test": "react-app-rewired test"
Add config-overrides.js
that looks like so (any utils can be used in override
):
const utils = require("customize-cra");
module.exports = utils.override(
utils.disableEsLint(),
utils.useBabelRc()
);
What happened to the scss
from before? What if I want to do the same setup?
It was removed in favor of Javascript styling through *.styles.js
files. It is common to use Javascript styles with material-ui, so following this pattern allows mirrors their examples/docs.
If you want to do the same setup as before, make sure you reference the scss files correctly (now that the build config is through react-scripts
). For example if you want to import styles/_base.scss
make sure you switch your imports like the following:
- @import 'base';
+ @import 'styles/_base.scss';
standard
)MIT © Prescott Prue