This article is part one in a weekly series on building a blog with React and is the foundation to the rest of this series.
Part One: Building a website with React and BulmaPart Two: Building a Blog with React and ContentfulPart Three: Import your Medium Feed into ReactPart Four: Adding a Redux to a React BlogPart Five: (In Progress) Replacing Redux Thunks with Redux Sagas
So lets install React
npm install -g create-react-app aaronklaser
cd aaronklaser
npm start
Next, lets install some of the stuff we know we will need:
React HelmetBulmaBulma ExtensionsMomentStyled Components
npm install bulma bulma-extensions moment react-helmet styled-components --save
This might not be completely necessary because we will mainly be using Styled Components, but this will make import Bulma much easier.
Now for the tricky part, we need to setup Sass but I don’t want to have to eject from create-react-app. I followed this tutorial.
npm install --save node-sass-chokidar
This will create .css files of our .scss files, which I’m not super crazy about but for now lets roll with it. We will want to ignore our css files in git, so add src/**/*.css to our .gitignore file.
Install npm-run-all so we can include our scss build in npm start
npm install --save npm-run-all
In the package.json in the npm scripts, add build-css and watch-css, build-js and watch-js, and modify build and start to use all our new scss stuff.
"scripts": {
"build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"start": "npm-run-all -p watch-css start-js",
"build-js": "react-scripts build",
"build": "npm-run-all build-css build-js",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
NOTE: It is important to include the --include-path ./src --include-path ./node-modules on both build-css and watch-css. This allows our code find libraries like Bulma in our node-modules folder.
We are going to include Bulma and any Bulma config in the index component. Start by rename index.css to index.scss then restart your app using npm start
If nothing blow up, and your project now includes index.scss and index.css then it means everything is working correctly.
Next, in your index.scss import bulma.
@import 'bulma/bulma';
@import 'bulma-extensions/extensions';
But, I’m going to do some additions steps to include an additional color because Bulma doesn’t include purple in its default colors and I kind of want purple.
// 1. Import the initial variables
@import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions";
// 2. Setup your Custom Colors
$purple: hsl(275,87%,45%);
$purple-invert: findColorInvert($purple);
// 3. Add new color variables to the color map.
@import "bulma/sass/utilities/derived-variables.sass";
$addColors: (
"purple":($purple, $purple-invert),
);
$colors: map-merge($colors, $addColors);
@import 'bulma/bulma';
@import 'bulma-extensions/extensions';
Everything should start up and your fonts on your standard create-react-app home page should look different.
Now, that we have all our setup done lets get some basic layout items in place using Bulma.
Let me take a quick minutes to talk about my file structure because its a little different then what you might be use to seeing.
I first found the idea of fractal file structures on an article on Hackernoon. The idea wasn’t very clear the first time I read the article but one thing popped out at me and it think I understand it. I could be completely wrong, but never the less, my method has worked VERY well from me other the last few months. It a kind of extension to the ReDucks pattern, except you create a kind of key value or parent child object structure with your files.
src
|- index.js
|- App.js
|- app
|- Layout.js
|- layout
|- Header.js
|- Content.js
|- Footer.js
|- User.js
|- user
|- Profile.js
|- PasswordReset.js
|- Address.js
|- components
|- SharedCompOne.js
|- SharedCompTwo.js
So you can see we are starting to group our code by module or feature, but we don’t create a billion folders for each component. Instead, we are keeping our components in their parent folder, using PascalCasing for each component name, and camelCasing for that components children folder. The only real exception here is App.js is technically a child of index, but I’m not sure that counts 🤔.
The benefit of this is that we can clearly see that Header, Content, and Footer are all components used in the Layout component which is used in the App component. If the filed structure was flatter like this:
/* This is NOT fractal */
src
|- index.js
|- App.js
|- layout
|- Layout.js
|- Header.js
|- Content.js
|- Footer.js
Then we don’t have any real indication to the hierarchy of components or which components Layout.js is even used on, where as the fractal pattern shows you exactly who uses what components and where. Its a clear parent child structure, and layouts can even include a shared folder for components only shared within layout children.
Remember how I mentioned key value structure earlier. This was the idea that made this all click in my head. So if you take my first example you think of it like this:
src: {
Index.js,
App.js: {
Layout.js: {
Header.js
Content.js
Footer.js
}
}
User.js: {
Profile.js
PasswordReset.js
Address.js
}
components: {
SharedCompOne.js
SharedCompTwo.js
}
}
Our site starts to look like JSON data… kinda, but you get the point. A “key” is always a component .js file. The “value” is as always folder with its key’s name and it contains all of the children components.
Where this really starts to shine is in your store, which I keep separate from my presentation layer (app). We can now start structuring our store like our data structure. This works REALLY well with NoSQL.
We will get more into this later.
App.js will call the Layout component and later include our Redux Store.
Layout.js will contain Site.js and Content.js, which are just a Styled Component wrapper for the site and content so we can get flexbox set up correctly, and Header.js and Footer.js components.
Heres the set up of those 5 files places into a single Gist for both our convenience.
<a href="https://medium.com/media/bdc0571966cd548f7757fd0bad898af4/href">https://medium.com/media/bdc0571966cd548f7757fd0bad898af4/href</a>
npm install react-router-dom
We are building a simple website/blog so we are going to use BrowserRouter. There are other option you can explore but this is not the thread for that ;)
At the root of our application in the src/index.js we are going wrap the app with the router.
import ReactDOM from 'react-dom'
import registerServiceWorker from './registerServiceWorker'
import { BrowserRouter as Router } from 'react-router-dom'
import App from './App'
import './index.css'
ReactDOM.render((
<Router>
<App />
</Router>
), document.getElementById('root'))
registerServiceWorker()
Then in the app folder, lets create two simple pages Home.js and Blog.js
import React from 'react'
const Home = () => (
<p>This is the Home Page</p>
)
export default Home
and
import React from 'react'
const Blog = () => (
<p>This is the Blog Page</p>
)
export default Blog
Now that we have some pages to route too, lets set up a Router component. For now, we are not going to try and do anything thing fancy with it, just the simple, straight forward Switch and Route.
In our layout folder add a new file call Router.js . This router will live inside the Content on our Layout.js file. This wills house our basic top level pages.
<Content>
<Router />
</Content>
Additional Routers components can be created in pages to add deeper nested routing. Our naming conventions for those in the future will be their parent component name and Router. Example, BlogRouter.js or HomeRoute.js. Remember, they will live in a folder called blog.
In our router component, we need at the Switch component and inside it, a list or our routes using the Route component.
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import Home from './../Home'
import Blog from './../Blog'
const Router = () => (
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/blog' component={Blog}/>
</Switch>
)
export default Router
And Finally, lets go set up our Header to call these routes.
For now, lets just make the changes for two pages we have routes for. We are going to modify our <a> on Blog and Logo Title nav items.
<NavLink
className="navbar-item"
to="/"
activeClassName="is-active"
>
<img
style={{
borderTopLeftRadius: '50%',
borderTopRightRadius: '50%',
borderBottomLeftRadius: '50%',
borderBottomRightRadius: '50%',
marginRight: 15
}}
src="https://media-exp2.licdn.com/mpr/mpr/shrinknp_400_400/AAEAAQAAAAAAAAU3AAAAJGE1MzYxNzYzLTE1NTUtNDEyYi04MzRjLTgzZjNkOGU0MGIzNg.jpg"
width="30px"
alt=""
/>
<span>AaronKlaser.com</span>
</NavLink>
and
<NavLink
className="navbar-item"
to="/blog"
activeClassName="is-active"
>
<span className="icon has-text-primary" style={{ marginRight: 5 }}
<i className="fas fa-code"></i>
</span>
Code Blog
</NavLink>
Notice that we added activeClassName=”is-active” This is because that is the Bulma nav active class. The cool thing about NavLink is its always checking for what page your on, and if your route matches that NavLink to it will automagically add is-active. If you don’t want to add an active class, just simple us the Link component instead.
So this is the basic frame of the application.
Review
More to come! I will update this post accordingly.