Note: This post doesn't portray that this is the only way to structure the code. There are many other great ways to do it. In fact, this method might have already been followed by most of you. This post also considers that you have some background knowledge about React and its ecosystem, also some common terminologies used in the web community
This is a two-part series, the next part will be posted soon.
Only to React?
Although the title says React code but this method can be applied across other front-end stacks too. I have used this method to build a Vue application too and it worked like a charm for me. I used React for this post because that's where I started for the first time.
How this will help?
I have worked in multiple teams and products. The major problems faced by a lot of new joiners is to understand the code structure. The first impression you make on your new joiners could be the last impression you make of the capabilities of your tech team. This method will help new joiners understand the code structure with the utmost simplicity.
Also, by following this way of code structuring, you would be able to avoid a lot of merge conflicts. Adding new features would be a cakewalk for any web-dev on your team. Also if you want to extract some code out to create a library out of it, you can do that too in very less time.
More in detail in the next part of this post series.
Why not use already built boilerplates?
We did start with cloning the best boilerplates out there but eventually had to tweak them accordingly to fit into our team's vision. So we ended up deleting a lot of code from that boilerplate code. The most common problem we faced while using some boilerplate code is to dump all the unnecessary code and configuration that isn't needed and while doing that, most of the time the code breaks, so we had to fix all the problems instead of building new features.
So we created our own boilerplate, just in case we need to kickstart a new front-end project in the future. I am saying that you should also stop using boilerplates created by well-known devs but this is just our strong opinion about what we want in our day-to-day coding sessions.
Creating your own boilerplate can also help you incrementally add libraries that you might later need while writing your production code. We like to keep our boilerplate code as lean(fewer libraries from the start) as possible because we don't want our bundle size to be high from the onset of our project.
Get started
First, let's use create-react-app
to scaffold our react app. We all know what does it do(if not that read it here). I am using it because I don't want this post to be longer than 2 posts, not deviating from our main topic and I am very lethargic too(truth revealed).
So after executing following command in the terminal in your workspace
npx create-react-app my-app
It creates a folder my-app with the following file structure,
Listing each directory and its uses,
-
node_modules
for your npm dependencies. -
public
directory contains all your static assets such as favicon, images, robots.txt, and your index.html which will get exported with right bundle names when you build your app. -
src
directory contains all your application code. -
build
directory will get created once you build the app. It contains all the transpiled js bundles, css, index.html, images, and favicons in it.
For the sake of this post, we will focus our attention on src
directory.
Let's look at what files the src
folder contains.
- The first file that loads into action is
index.js
which imports every other needed file to bootstrap this application. -
index.css
contains global css styles. -
App.js
,App.css
andApp.text.js
are all related to the App component which is our root component. Everything that renders on our screen when we runyarn start
command is in the App component. - Rest of the files you can ignore for now especially
serviceWorker.js
We can add all our Application code in our App Component file but it would give you nightmares later on. Also, we would want our SPA to have routes for our application to react to URL changes.
Let's consider we want to build an application which has 4 routes
- Login
- Dashboard
- Listing
- Profile
When the user first lands on our application, he/she sees the login screen first, after login we redirect our app to Dashboard where the user can navigate to Listing and Profile. Don't want to overdose you with what features each route will have. This post is already quite long for you and I don't want you to get bored.
Moving on, we will need some kind of routing logic for our routes. I am using react-router-dom
for representational purposes. First I add it as a dependency using the command yarn add react-router-dom
. Make sure it has been added to your package.json
dependencies.
I don't like to keep the App component lingering around in the src
folder like that, I will create a screens
folder. I will come back to why I named it like that. Inside that screens
folder, create another folder named App
. Inside the App
folder create a file named index.js
, this we will use for exporting App code to whoever wants to consume it.
Now you create a components
folder under src/screens/App
folder. Move App.js
, App.css
and App.test.js
files to recently created components
folder. Now your file structure will look like this.
I will now export the App component from src/screens/App/index.js
file.
I will now delete the entire jsx code from App component to make things easier for us.
We will make file location changes for App component import in the src/index.js
file. Also, removing the references to index.css
and serviceWorker.js
files which we don't need in our context. The src/index.js
file looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import App from 'screens/App';
ReactDOM.render(<App />, document.getElementById('root'));
Now if you save and look at the application, it will be just a white screen with text - App component written in the top center of the screen.
Now let's create another folder under src
directory named shared
. This directory will contain all the shared utilities, constants, components, and other stuff that needs to be shared with your entire Application code. Right now I am creating constants for our routes.
Now we go ahead and create route links in our App component. We import our constants and create some anchor tags to those routes.
Before we do that, we need to solve add a file .env
at the root. Some of you must have seen a discrepancy in one of the changes I made above while editing src/index.js
. I had referenced App component like this,
import App from 'screens/App';
And not like this,
import App from './screens/App';
It's because I added the .env
file at the root folder which helps us resolve modules in the src/
folder without having to reference files using ../../../
from deep inside the file directory. Create a file .env
at the root and paste the following content in it and then restart our application using yarn start
.
NODE_PATH='src/'
As a direct result of this, we can now reference shared constants(also any future shared utility) from anywhere our application like following,
import routeConstants from 'shared/constants/routes';
Without worrying about those ../../../
After this, we import routeConstants into our App component and add 4 Link tags from the react-router-dom
library just below the App title. I also added a bunch of other components required for our routes to work.
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import './App.css';
import routeConstants from 'shared/constants/routes';
const {
LOGIN,
DASHBOARD,
LISTING,
PROFILE,
} = routeConstants;
function App() {
return (
<Router>
<div className="App">
<h1>App Component</h1>
<ul className="App-nav-list">
<li className="App-nav-item">
<Link to={LOGIN.route}>{LOGIN.name}</Link>
</li>
<li className="App-nav-item">
<Link to={DASHBOARD.route}>{DASHBOARD.name}</Link>
</li>
<li className="App-nav-item">
<Link to={LISTING.route}>{LISTING.name}</Link>
</li>
<li className="App-nav-item">
<Link to={PROFILE.route}>{PROFILE.name}</Link>
</li>
</ul>
<Switch>
<Route exact path={LOGIN.route}>
<h1>{LOGIN.name}</h1>
</Route>
<Route path={DASHBOARD.route}>
<h1>{DASHBOARD.name}</h1>
</Route>
<Route path={LISTING.route}>
<h1>{LISTING.name}</h1>
</Route>
<Route path={PROFILE.route}>
<h1>{PROFILE.name}</h1>
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
Now our App looks like this,
Although I dumped everything inside the App component here. In the next post, I will start distributing each route component code into its own separate space so that you get the gist how the final file structure actually works.
Please do leave comments about the pace, language as well as contents of this post. This is my first post and I might have been grammatically incorrect. Also please do comment if you are eagerly waiting for the next post, it will encourage me to post the next one early. :)
Top comments (3)
For anyone running into issues with absolute imports using the .env file:
The support for NODE_PATH has been removed with create-react-app v4: github.com/facebook/create-react-a...
Use jsconfig.json instead: create-react-app.dev/docs/importin...
Suraj... I've been thinking there had to be a way to do this for so long. You just saved my life! Thank you for posting this!
Wow, that's so good to hear. I am happy I could be of any help. Thanks for reading.