I'm setting up a React project with my team that will use mobX as state manager, plus TypeScript.
I've seen a common pattern in the casing and naming patterns in React Projects:
camelCase
or kebab-case
components
folder): PascalCase
Is there a formal convention for folder/file naming in react? If not, is there a style guide on which this pattern is based? Or a reason why this one is used most of the times?
In React applications, there are 3 most common naming conventions: Camel case for file names, and pascal case for component names. Kebab case for file names, and pascal case for component names. Pascal case for both file names, and component names.
Components are the building blocks of any react project. This folder consists of a collection of UI components like buttons, modals, inputs, loader, etc., that can be used across various files in the project. Each component should consist of a test file to do a unit test as it will be widely used in the project.
The other method is also straightforward, you can check the React app version by heading over to node_modules/react/cjs/react. development. js. You can see the react project version in the commented section as showing given below.
Just to add my two cents. As others have stated, file structure is unopinionated. However, component naming is not. They should be PascalCase
for React to know whether or not you're using a function
, class
or an HTMLelement
†.
For example:
class input extends Component {...}
Bad! Why? Because React doesn't know whether or not you're trying to use the input
element or the class-based component.
That's why you'll see PascalCase components:
class Input extends Component {...}
† There is one exception, where you can use dot notation
. For example, if you had multiple exports and you import them all as fields
, then you could do something like:
component/fields/index.js
import React, { Component } from 'react'; export class input extends Component { state = { value: "" }; handleChange = ({ target: { value } }) => { this.setState({ value }); }; render = () => ( <input type="text" value={this.state.value} onChange={this.handleChange} /> ); } export class textarea extends Component { state = { value: "" }; handleChange = ({ target: { value } }) => { this.setState({ value }); }; render = () => ( <textarea type="text" value={this.state.value} onChange={this.handleChange} /> ); }
components/App/index.js
import React, { Fragment } from 'react'; import * as fields from "../fields"; const App = () => ( <Fragment> <fields.input /> <fields.textarea /> <Fragment> ); export default App;
As a general rule of thumb, I avoid dot notation
altogether. It feels clumsy and may confuse other developers who don't know how fields
is structured. Plus, I'm not a fan of stacking multiple components within 1 file and then importing them as a bunch. In addition, the file can become quite large and cumbersome to navigate and debug (more on this below).
That said, to keep my structure simple, I like to keep main directories lowercase:
├── dist // compiled application files to be served | ├── css | | ├── main.[contenthash:8].css | | └── main.[contenthash:8].css.map | ├── js | | ├── main.[hash].js // depending on app size, this may contain multiple js files for code splitting | | └── main.[hash].js.map | ├── media | | └── [hash].[ext] // static assets like fonts and images | └── favicon.ico | └── index.html | ├── config // supporting "webpackdevserver" configuration files | ├── devServer.js | ├── envs.js | ├── optimization.js | ├── output.js | ├── paths.js | ├── plugins.js | └── rules.js | ├── public | ├── favicon.ico | └── index.html | ├── src | ├── actions // redux actions | ├── components // stateful and stateless reusable components that just display "stuff" -- stateful components change and manipulate the UI | ├── containers // stateful components that utilize the reusable "components" to CRUD data and/or are connected to redux | ├── images | ├── pages // utilize components/containers to display something when visiting a "/route" | ├── reducers // redux reducers | ├── root // aka "<App />" that combines "routes", redux and other top-level supporting files into one place | ├── routes // assigns "pages" to a "/route" | ├── styles // shared and/or global styles used by all "components" | ├── types // redux types | ├── utils // supporting app files: like test setup, custom polyfills, axios configurations, ...etc | └── index.js // a simple file that "ReactDOM.render"s the "App" | ├── server.js // express setup to serve the "dist" folder └── webpack.config.js
Then within the component
folder, I'll PascalCase my components to represent something like this:
└── components └── Input ├── __tests__ | └── Input.test.js // jest unit tests for "index.js" ├── index.js // all required code/styles to be exported └── styles.scss // styles required by "index.js"
Why this structure?
Input
is self-contained within this folder. Therefore, I can hand it off to someone and they can slot it in their application and just use it.index.js
, so it's very easy to import without traversing a ton of nested files: import Input from 'components/Input';
(also, no need to specify the exact js
file to use since "index.js" contains all required code).Drawbacks:
index.js
nomenclature so it can be a bit confusing at first as to what "index.js" has failed.Another approach I used to do was:
└── components ├── input // lowercase name to delineate it's a "pure" function -- the actual function will be a PascalCased "Input" | ├── input.test.js // jest unit tests for "input.js" | ├── input.js // all required code/styles to be exported | └── styles.scss // styles required by "input.js" | └── Sidebar // PascalCase because it's a "class" ├── Sidebar.test.js // jest unit tests for "Sidebar.js" ├── Sidebar.js // all required code/styles to be exported └── styles.scss // styles required by "Sidebar.js"
Why this structure?
Input
is self-contained within this folder. Therefore, I can hand it off to someone and they can slot it in their application and just use it.function
or a class
.Drawbacks:
import Input from 'components/input/input.js';
Other general guidelines:
Example of a default exported anonymous function:
export default () => ( <p>Anonymous Function</p> );
Why? Because when testing, the function will show up in enzyme as:
<_default />
When you have multiple anonymous functions within a component, which one is which!?
<_default /> <_default /> <_default />
More often than not, I've found that most components will fall under 100 lines or so when properly optimized. Worst case scenario is I'll have to create small subcomponents to supplement the main component. But! Much easier to read and debug.
What's easier to read:
Example #1 (34 lines with supplemental child components)
Example #2 (318 lines of everything)
Example #1 mimics reading a book. Multiple pages that when glued together create an easy-to-read experience. Versus Example #2 which reads a like a mile-long scroll that can be easy to get lost in!
This one can be confusing, but it all depends on how you're applying styles. If you're just importing the style like so:
import "./styles.css";
Then you can use snake-case:
<input className="snake-case" type="text" value="" onChange={this.handleChange} />
However, if you're using css modules
, then you'll need to use camelCase:
import { camelCaseClassName } from "./styles.css";
Why? Because bundlers (like Webpack) don't support snake-case imports:
<input className={camelCaseClassName} type="text" value="" onChange={this.handleChange} />
Conclusion: There are many ways to create a folder structure with a few tips and tricks to maintain a logical flow. Just pick one that works best for you AND doesn't interfere with the person working beside you!
In other words, K.I.S.S === "Keep it simple, silly!"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With