Please find below an outline of our current architecture. As the frontend landscape changes at mindboggling speed, if you read this and think „I know of a better way to do XYZ!“ we highly encourage you to discuss it with the team.
General | JavaScript | CSS | Testing |
---|---|---|---|
↓ Tooling | ↓ Javascript Overview | ↓ CSS & Styling Overview | ↓ Testing Overview |
↓ npm Commands | ImmutableJS | PostCSS | Unit Testing |
↓ Deployment | Redux Saga | CSS Modules | Component Testing |
↓ File Glossary | Routing | Redux Testing | |
i18n |
Powered by React ➝. (➝ marks external links)
We differentiate between dumb, reusable and ideally functional stateless components
and smart stateful containers
.
Read Dan Abramov's explanation to this approach ➝.
We organize code primarily by feature, not by functionality. That means we co-locate all component-specific code in the same folder. An example for a container component:
├ CoolContainer
├── index.js // the actual react component
├── actions.js // container-specific actions
├── constants.js // container-specific constants
├── reducer.js // container-specific reducer
├── sagas.js // container-specific sagas
├── selectors.js // container-specific selectors
├── styles.css // container-specific styles
└── tests/ // container-specific tests
├—— index.test.js
├—— actions.test.js
├—— reducer.test.js
├—— sagas.test.js
└── selectors.test.js
We do keep tests in a subfolder to make it easier to differentiate between actions.js
and actions.test.js
etc.
We manage application state with Redux ➝, make it immutable with ImmutableJS ➝, improve performance with reselect ➝ and handle side effects like data fetching or login with redux-saga ➝.
If you haven't worked with Redux, it's highly recommended to read through the excellent official documentation ➝ and/or watch these free beginner ➝ and advanced ➝ video series by creator Dan Abramov ➝.
We have more detailed docs on ImmutableJS, reselect and redux-saga.
We use react-router ➝ for routing and keep the URL state synced to redux with react-router-redux ➝. See a short explanation and some code examples in the routing doc.
We use react-intl ➝ for internationalization and pluralization. Read the i18n doc for implementation details.
Tool | Description |
---|---|
PostCSS ➝ | We use PostCSS for all our CSS transformation needs |
cssnext ➝ | The latest CSS features without cross-browser pain |
CSSModules ➝ | Avoid global scope littering and keep styles local |
Autoprefixer ➝ | Automagically™️ adds vendor prefixes |
sanitize.css ➝ | A modern take on normalize.css |
cssnano ➝ | CSS compressor/minifier/optimizer |
stylelint ➝ | Catches bugs and keeps our styles consistent |
- We write our styles as modular and composable as possible.
- No special
/css
directory. - We co-locate component-specific styles with the actual component code.
Read further docs on CSS Modules and PostCSS.
Tool | Description |
---|---|
Karma ➝ | Test Runner |
Mocha ➝ | Test Framework |
Expect ➝ | Assertion Library |
Enzyme ➝ | Shallow Component Rendering |
Sinon.JS ➝ | Spies / Stubs / Mocks |
Cheerio ➝ | Browserless DOM testing using Node |
Coveralls ➝ | Test Coverage |
Snyk ➝ | Dependency Vulnerability Checker |
Type | Description |
---|---|
Unit Testing | We use standard unit tests for all non-component code like actions, reducers, sagas, helpers, business logic etc. |
Component Testing | We use enzyme to test the correct rendering and behavior of our React components |
Integration/E2E Testing | We consciously decide to start without e2e tests to keep overhead low. It will make sense to add later on when functionality is more well-defined and less likely to change often. |
Remote Testing | We use ngrok ➝ to enable remote testers to access our local machine on demand. |
TODO: Add details on deployment once we have a deployment target…
- ES6/7 support thanks to
babel-react
,babel-preset-latest
andbabel-preset-stage-0
-
Bundling
-
Native ES6 Modules
-
Hot Module Reloading incl. state preservation
-
Treeshaking
-
Webpack's image-loader ➝ optimizes every PNG, JPEG, GIF and SVG image.
-
Special images in HTML files
If you specify your images in the
.html
files using the<img>
tag, everything will work fine. The problem comes up if you try to include images using anything except that tag, like meta tags:<meta property="og:image" content="img/yourimg.png" />
The webpack
html-loader
does not recognise this as an image file and will not transfer the image to the build folder. To get webpack to transfer them, you have to import them with the file loader in your JavaScript somewhere, e.g.:import 'file?name=[name].[ext]!../img/yourimg.png';
Then webpack will correctly transfer the image to the build folder.
We include a generator for components, containers, sagas, routes and selectors.
Run npm run generate
to choose from the available generators, and automatically
add new parts of your application!
Note: If you want to skip the generator selection process,
npm run generate <generator>
also works. (e.g.npm run generate route
)
We use Plop ➝ to generate new components from predefined templates. You can find all the logic and templates for the generation in internals/generators
Redux devtools ➝ should work out of the box.
If you import web fonts naively, you'll either have blank page until the fonts are downloaded or face an ugly FOUC (Flash of unstyled content). Both scenarios aren't ideal.
FontFaceObserver ➝ adds a class
to the body
when the fonts have loaded. (see app.js
and App/styles.css
)
-
Either add the
@font-face
declaration toApp/styles.css
or add a<link>
tag to theindex.html
. -
In
App/styles.css
, specify your initialfont-family
in thebody
tag with only web-save fonts. In thebody.jsFontLoaded
tag, specify yourfont-family
stack with your web font. -
In
app.js
add a<fontName>Observer
for your font.
For routing to work correctly, we'll need to set up some server configs.
This app includes a .htaccess
file that does two things:
- Redirect all traffic to HTTPS because ServiceWorker only works for encrypted traffic.
- Rewrite all pages (e.g.
yourdomain.com/subpage
) toyourdomain.com/index.html
to letreact-router
take care of presenting the correct page.
Note: For performance reasons you should probably adapt it to run as a static
.conf
file (typically under/etc/apache2/sites-enabled
or similar) so that your server doesn't have to apply its rules dynamically per request)
Also it includes a .nginx.conf
file that does the same on Nginx server.
Availability without network connection is powered by a ServiceWorker with a fallback to AppCache for older browsers. All files are included automatically. No manual intervention needed thanks to Webpack's Offline Plugin ➝
After repeat visits to the website, users will get a prompt to add the application to their homescreen. Combined with offline caching, this means our web app can be used exactly like a native application (without the limitations of an app store).
The name and icon to be displayed are set in the app/manifest.json
.
npm start
Starts the development server at localhost:3000
. Changes in the application code will be hot-reloaded.
npm run start:tunnel
Starts the development server and tunnels it with ngrok
, making the website
available to the public internet. Useful for testing on different devices in different locations.
npm run start:prod
Starts the production server, configured for optimal performance: assets are minified and served gzipped.
npm start -- --port 5000
npm run build
Prepares your app for deployment. Optimizes and minifies all files, treeshakes the JavaScript piping them to a folder called build
. Upload the contents of build
to your web server to see your work live!
npm run clean:all
Removes ./build
, ./stats.json
and ./coverage
npm run generate
Allows you to auto-generate boilerplate code for common parts of the
application, specifically component
s, container
s, and route
s. You can
also run npm run generate <part>
to skip the first selection. (e.g. npm run generate container
)
npm test
Tests your application with the unit tests specified in the *.test.js
files
throughout the application. All the test
commands allow an optional -- --grep string
argument to filter
the tests ran by Karma. Useful if you need to run a specific test only.
# Run only the Button component tests
npm run test:watch -- --grep Button
To choose the browser to run your unit tests in (Chrome by default), run one of the following commands:
npm run test:firefox
npm run test:safari
Windows only!
npm run test:ie
npm run test:watch
Watches changes to your application and reruns tests whenever a file changes.
npm run pagespeed
With the remote server running (i.e. while npm run start:prod
is running in
another terminal session), enter this command to run Google PageSpeed Insights
and get a performance check right in your terminal!
npm run analyze
This command will generate a stats.json
file from your production build, which
you can upload to the webpack analyzer. This
analyzer will visualize your dependencies and chunks with detailed statistics
about the bundle size.
npm run lint
Lints your JavaScript and CSS.
npm run lint:js
Only lints your JavaScript.
npm run lint:css
Only lints your CSS.
#####Why not let webpack auto-lint on filesave, you ask?
We've found that this can degrade the build performance quite a bit. "Live linting" should be a concern of the IDE so setting this up is the individual developers responsibility. Lint does run on precommit
so there is a safety net.
.
├── app # The application source code
│ ├── assets # Fonts & images
│ ├── components # „Dumb“ Components
│ ├── containers # „Smart“ Containers
│ ├── tests # Global tests e.g. for store, rootReducer etc.
│ ├── translations # [Generated] i18n locale files (`npm run extract-intl`)
│ └── utils # Global helpers and utility-functions like API-helpers etc.
├── dist # [Generated] Build output
├── docs # Documentation on specific technologies/frameworks etc.
├── internals # Tooling & Configuration
│ ├── generators # Generator templates for new components, containers, routes etc.
│ ├── scripts # Utility and tooling scripts like extract-i18n, pagespeed analysis etc.
│ ├── testing # Test config for Karma etc.
│ └── webpack # Webpack config
├── node_modules # [Generated] NPM packages
├── .editorconfig # Editor styleguide for all team members
├── .gitattributes # Normalizes how git handles certain files
├── .gitignore # Tells git which files to ignore
├── .snyk # snyk.io policy file
├── CHANGELOG # [Generated] Auto-generated CHANGELOG on version bump summarizing changes since the last version
├── package.json # Lists project's dependencies. Configures babel/eslint/stylelint. Specifies npm tasks
└── README.md # This file