├── .babelrc ├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── .stylelintrc ├── LICENSE ├── README.md ├── package.json ├── public └── index.html ├── src ├── App.css ├── App.js ├── components │ └── Button │ │ ├── Button.css │ │ ├── Button.js │ │ └── index.js ├── index.js └── stories │ ├── Button │ └── index.js │ └── index.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": ["react-hot-loader/babel"] 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true 4 | }, 5 | "parser": "babel-eslint", 6 | "parserOptions": { 7 | "sourceType": "module", 8 | "ecmaVersion": 6, 9 | "ecmaFeatures": { 10 | "jsx": true 11 | } 12 | }, 13 | "rules": { 14 | "semi": ["error", "never"], 15 | "no-multiple-empty-lines": ["error", { "max": 1 }], 16 | "max-params": ["error", { "max": 5 }], 17 | "comma-dangle": ["error", "never"], 18 | "function-paren-newline": ["error", "multiline"], 19 | "max-len": [ "error", 20 | { 21 | "code": 80, 22 | "tabWidth": 2, 23 | "ignoreComments": true, 24 | "ignoreTrailingComments": true, 25 | "ignoreUrls": true, 26 | "ignoreStrings": true, 27 | "ignoreTemplateLiterals": true, 28 | "ignoreRegExpLiterals": true 29 | } 30 | ], 31 | "quotes": [ "error", "single", 32 | { 33 | "allowTemplateLiterals": true, 34 | "avoidEscape": true 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # build 64 | dist/ 65 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register' 2 | import '@storybook/addon-a11y/register' 3 | import '@storybook/addon-backgrounds/register' 4 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { addParameters, configure } from '@storybook/react' 2 | 3 | function loadStories() { 4 | require('../src/stories') 5 | } 6 | 7 | addParameters({ 8 | backgrounds: [ 9 | { name: 'Light', value: '#cdd6e0', default: true }, 10 | { name: 'Dark', value: '#191f29' }, 11 | ], 12 | }) 13 | 14 | configure(loadStories, module) 15 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const custom = require('../webpack.config.js') 2 | 3 | module.exports = async ({ config, mode }) => { 4 | return { 5 | ...config, 6 | module: { 7 | ...config.module, 8 | rules: custom.module.rules 9 | }, 10 | resolve: { 11 | ...config.resolve, 12 | modules: custom.resolve.modules 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "stylelint-order" 4 | ], 5 | "rules": { 6 | "block-no-empty": true, 7 | "color-no-invalid-hex": true, 8 | "comment-empty-line-before": [ "always", { 9 | "ignore": ["stylelint-commands", "after-comment"] 10 | } ], 11 | "comment-whitespace-inside": "always", 12 | "declaration-colon-space-after": "always", 13 | "declaration-colon-space-before": "never", 14 | "function-url-quotes": "always", 15 | "indentation": 2, 16 | "max-empty-lines": 2, 17 | "max-line-length": [ 80, { 18 | "ignore": ["comments"] 19 | } ], 20 | "no-extra-semicolons": true, 21 | "order/properties-alphabetical-order": true, 22 | "property-case": "lower", 23 | "rule-empty-line-before": [ "always", { 24 | "except": ["first-nested"], 25 | "ignore": ["after-comment"] 26 | } ], 27 | "selector-list-comma-newline-after": "always", 28 | "selector-max-class": 5, 29 | "selector-max-type": 5, 30 | "selector-pseudo-element-case": "lower", 31 | "unit-case": "lower" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Lucas J S 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Boilerplate 2 |

3 | David 4 | GitHub repo size 5 | GitHub 6 | GitHub last commit 7 |

8 | 9 | Stack: 10 | - [React](https://github.com/facebook/react) 11 | - [React Hot Loader](https://github.com/gaearon/react-hot-loader) 12 | - [PropTypes](https://github.com/facebook/prop-types) 13 | - [Babel](https://github.com/babel/babel) 14 | - [CSS Modules](https://github.com/css-modules/css-modules) 15 | - [PostCSS](https://github.com/postcss/postcss) 16 | - [postcss-preset-env](https://github.com/csstools/postcss-preset-env) 17 | - [Webpack](https://github.com/webpack/webpack) 18 | - [ESLint](https://github.com/eslint/eslint) 19 | - [stylelint](https://github.com/stylelint/stylelint) 20 | - [Storybook](https://github.com/storybookjs/storybook) 21 | 22 | ## Run the project locally 23 | 24 | **1 -** Clone the project and install the dependencies: 25 | 26 | ``` 27 | $ git clone https://github.com/lucasjs/react-boilerplate.git 28 | $ cd react-boilerplate 29 | $ npm install 30 | ``` 31 | 32 | **2 -** Run development mode: 33 | 34 | ``` 35 | $ npm start 36 | ``` 37 | Open [http://localhost:8080](http://localhost:8080) to view it in the browser. 38 | 39 | ## Scripts 40 | 41 | Build: 42 | 43 | ``` 44 | $ npm run build 45 | ``` 46 | 47 | Storybook: 48 | 49 | ``` 50 | $ npm run storybook 51 | ``` 52 | 53 | ## Folders 54 | 55 | . 56 | ├── README.md 57 | ├── LICENSE 58 | ├── .storybook/ 59 | | ├── addons.js 60 | | ├── config.js 61 | | └── webpack.config.js 62 | ├── public/ 63 | | └── index.html 64 | ├── src/ 65 | | ├── components 66 | | ├── stories/ 67 | | | └── index.js 68 | | ├── App.css 69 | | ├── App.js 70 | | └── index.js 71 | ├── package-lock.json 72 | ├── package.json 73 | ├── webpack.config.js 74 | ├── yarn.lock 75 | ├── .babelrc 76 | ├── .editorconfig 77 | ├── .eslintrc.json 78 | ├── .stylelintrc 79 | └── .gitignore 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-boilerplate", 3 | "version": "1.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --compress --open --mode development --hot", 9 | "build": "webpack --mode production", 10 | "storybook": "start-storybook -p 9009 -s public", 11 | "build-storybook": "build-storybook -s public" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/lucasjs/react-boilerplate.git" 16 | }, 17 | "keywords": [ 18 | "react" 19 | ], 20 | "author": "Lucas JS", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/lucasjs/react-boilerplate/issues" 24 | }, 25 | "homepage": "https://github.com/lucasjs/react-boilerplate#readme", 26 | "dependencies": { 27 | "@hot-loader/react-dom": "^16.8.6", 28 | "babel-cli": "^6.26.0", 29 | "babel-preset-react-app": "^9.1.2", 30 | "prop-types": "^15.7.2", 31 | "react": "^16.8.6", 32 | "react-dom": "^16.8.6", 33 | "react-hot-loader": "^4.12.14" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.6.2", 37 | "@babel/preset-env": "^7.6.2", 38 | "@babel/preset-react": "^7.0.0", 39 | "@storybook/addon-a11y": "^5.2.1", 40 | "@storybook/addon-actions": "^5.2.1", 41 | "@storybook/addon-backgrounds": "^5.2.1", 42 | "@storybook/addons": "^5.1.11", 43 | "@storybook/react": "^5.2.1", 44 | "babel-eslint": "^10.0.3", 45 | "babel-loader": "^8.0.6", 46 | "css-loader": "^3.2.0", 47 | "eslint": "^6.5.1", 48 | "eslint-loader": "^4.0.2", 49 | "file-loader": "^6.0.0", 50 | "html-webpack-plugin": "^4.2.0", 51 | "postcss-import": "^12.0.1", 52 | "postcss-loader": "^3.0.0", 53 | "postcss-preset-env": "^6.7.0", 54 | "style-loader": "^1.0.0", 55 | "stylelint": "^13.3.3", 56 | "stylelint-order": "^4.0.0", 57 | "stylelint-webpack-plugin": "^1.0.1", 58 | "url-loader": "^4.1.0", 59 | "webpack": "^4.41.0", 60 | "webpack-cli": "^3.3.9", 61 | "webpack-dev-server": "^3.8.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | React Boilerplate 9 | 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | font-family: Arial, Helvetica, sans-serif; 3 | margin: 1rem; 4 | } 5 | 6 | .title { 7 | color: blue; 8 | } 9 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState } from 'react' 2 | import { hot } from 'react-hot-loader/root' 3 | import styles from './App.css' 4 | import Button from 'components/Button' 5 | 6 | const App = () => { 7 | const [name] = useState('Lucas') 8 | 9 | return ( 10 |
11 |

Hello, {name}!

12 | 17 |
18 | ) 19 | } 20 | 21 | export default hot(App) 22 | -------------------------------------------------------------------------------- /src/components/Button/Button.css: -------------------------------------------------------------------------------- 1 | .button { 2 | background-color: #2196f3; 3 | border: 1px solid #1ea7fd; 4 | border-radius: 5px; 5 | color: #12145d; 6 | cursor: pointer; 7 | font-size: 15px; 8 | padding: 5px 10px; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import styles from './Button.css' 4 | 5 | const Button = ({ 6 | children, 7 | handleClick 8 | }) => { 9 | return ( 10 | 16 | ) 17 | } 18 | 19 | Button.defaultProps = { 20 | handleClick: null 21 | } 22 | 23 | Button.propTypes = { 24 | children: PropTypes.string.isRequired, 25 | handleClick: PropTypes.func 26 | } 27 | 28 | export default Button 29 | -------------------------------------------------------------------------------- /src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Button' 2 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App.js' 4 | 5 | ReactDOM.render(, document.getElementById('root')) 6 | -------------------------------------------------------------------------------- /src/stories/Button/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { storiesOf, addDecorator } from '@storybook/react' 3 | import { withA11y } from '@storybook/addon-a11y' 4 | import { action } from '@storybook/addon-actions' 5 | 6 | import Button from 'components/Button' 7 | 8 | addDecorator(withA11y) 9 | 10 | storiesOf('Button', module) 11 | .add('Default', () => ( 12 | 15 | )) 16 | -------------------------------------------------------------------------------- /src/stories/index.js: -------------------------------------------------------------------------------- 1 | import './Button' 2 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const StylelintPlugin = require('stylelint-webpack-plugin') 4 | const postcssPresetEnv = require('postcss-preset-env') 5 | 6 | module.exports = { 7 | plugins: [ 8 | // HTML 9 | new HtmlWebpackPlugin({ 10 | template: path.resolve(__dirname, 'public/index.html') 11 | }), 12 | // stylelint 13 | new StylelintPlugin({ 14 | configFile: '.stylelintrc', 15 | context: 'src', 16 | files: '**/*.css' 17 | }) 18 | ], 19 | entry: [ 20 | 'react-hot-loader/patch', 21 | './src' 22 | ], 23 | output: { 24 | path: path.resolve(__dirname, 'dist'), 25 | filename: 'bundle.js' 26 | }, 27 | module: { 28 | rules: [ 29 | // ESLint 30 | { 31 | enforce: 'pre', 32 | test: /\.(js|jsx)$/, 33 | exclude: /node_modules/, 34 | loader: 'eslint-loader', 35 | options: { 36 | emitError: true 37 | } 38 | }, 39 | // Babel 40 | { 41 | test: /\.(js|jsx)$/, 42 | include: /src/, 43 | exclude: /node_modules/, 44 | loader: 'babel-loader' 45 | }, 46 | // Images 47 | { 48 | test: /\.(png|jpe?g|gif)$/, 49 | include: /src/, 50 | exclude: /node_modules/, 51 | use: 'file-loader' 52 | }, 53 | // CSS 54 | { 55 | test: /\.css$/, 56 | use: [ 57 | { 58 | loader: 'style-loader', 59 | options: { 60 | injectType: 'singletonStyleTag' 61 | } 62 | }, 63 | // CSS Modules 64 | { 65 | loader: 'css-loader', 66 | options: { 67 | importLoaders: 1, 68 | modules: { 69 | localIdentName: '[name]-[local]--[hash:base64:5]' 70 | }, 71 | sourceMap: true 72 | } 73 | }, 74 | // Post CSS Preset Env 75 | { 76 | loader: 'postcss-loader', 77 | options: { 78 | ident: 'postcss', 79 | plugins: () => [ 80 | postcssPresetEnv({ 81 | stage: 0, 82 | browsers: 'last 4 versions' 83 | }) 84 | ], 85 | sourceMap: true 86 | } 87 | } 88 | ] 89 | } 90 | ] 91 | }, 92 | // Hot Loader 93 | resolve: { 94 | alias: { 95 | 'react-dom': '@hot-loader/react-dom' 96 | }, 97 | modules: [ 98 | path.resolve(__dirname + '/src'), 99 | path.resolve(__dirname + '/node_modules') 100 | ] 101 | }, 102 | devServer: { 103 | contentBase: path.resolve(__dirname, 'public'), 104 | compress: true, 105 | hot: true 106 | } 107 | } 108 | --------------------------------------------------------------------------------