This is my first ever blog post!
In this post, I'm going to be describing the steps I took in designing, developing, and deploying this web site.
The idea of the web site was simple: to be an online portfolio for me. It supposed to describe me as a person, and show my works and projects. Basically it was a static site with some images, and just paragraphs of text. There will need to be:
Now, this could be done with simple HTML and CSS, but I also wanted a blog section for my site; I wanted to write blog posts about my software developer journey.
I then decided to use React.JS as the front end framework for developing the client. It provides benefits such as:
I should also add, that I wanted to use React.JS in the beginning anyway, because I wanted to practice it. Knowing how to use React.JS might come useful in my work, so I though it was a good idea to try it out.
I start my development off by listing all the components I need in the web site; each page is a component.
I've also decided to use reactstrap to use bootstrap 4 components in my web site. Using reactstrap allows me to make the web site responsive to both mobile, tablet, and desktop view. Since I've got bootstrap components in my web site, I can also use the navigation bar component, which eliminate the need for me to have to implement my own nav bar.
The first thing I implement is the navigation bar, and styling using CSS. This is when the first obstical shows up. I noticed that whatever style I make to the navigation bar is being overridden. I can see that the style was applied by was also being overridden by using the chrome dev tools and inspect the navigation bar element. After some extend of time, accompanied with many head scratches, I've found a solution to why the style was being overridden. The problem was that, in the index.js
file, I've imported the bootstrap.min.css
after the I've imported App.jsx
. This causes the style of bootstrap.min.css to override the styles of App.jsx
and its childrens. To fix this issue, I had to simply move the order of import up a bit for bootstrap.min.css
; do like so below:
index.js
0
1
2
3
4
5
6
7
8
9
import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App'; // <-- Now, anything in App.jsx and its children components will override bootstrap.min.css
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
The next thing to do is to add react router to create routes to other components. Here, I encounter another problem; when I click a NavLink element which route me to another component, the whole page is loaded. I don't want the page to load, but rather to use the react router to route to the component page. After a quick search on the internet, I've found that the reactstrap NavLink component provide its own property to deal with this issue. Simply add Link
from react-router-dom
to the NavLink
's tag
property, like so below:
NavigationBar.jsx
0
1
2
3
4
render() {
return (
<NavLink tag={Link} to="/about" >About</NavLink>
)
}
Back in 2018, I've experimented with Gatsby which can render Markdown files as a page on the site. I thought this was a good idea to apply to my web site as well using create-react-app. In my web site there is a section for blog posts, and to be able to write each post in Markdown and render it would be useful. And of course there's an npm module for that; react-markdown is a node module that render Markdown as pure React components. With React Syntax Highlighter, a syntax highlighting component for React, to render the code block in the Markdown file. I also pass the post title through react router url.
CodeBlock.jsx
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React from 'react';
import PropTypes from 'prop-types';
import SyntaxHighlighter from 'react-syntax-highlighter';
class CodeBlock extends React.PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
language: PropTypes.string,
}
static defaultProps = {
language: null,
}
render() {
const { language, value } = this.props;
return (
<SyntaxHighlighter
language={language}
showLineNumbers={true}
>
{value}
</SyntaxHighlighter>
);
}
}
export default CodeBlock;
BlogTemplate.jsx
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import React, { Component } from 'react';
import './BlogTemplate.css';
import ReactMarkdown from 'react-markdown';
import { Row, Col } from 'reactstrap';
import CodeBlock from './CodeBlock';
class BlogTemplate extends Component {
constructor(props) {
super(props);
this.state = {
markdown: null
};
}
componentWillMount() {
const { postname } = this.props.match.params;
const markdownFile = require(`./posts/${postname}.md`);
fetch(markdownFile)
.then((response) => {
return response.text();
})
.then((markdown) => {
this.setState({
markdown: markdown
});
});
}
render() {
return (
<div className="page-blog" >
<Row className="desc" >
<Col sm="12" md={{ size: 8, offset: 2}} >
<ReactMarkdown
source={ this.state.markdown }
renderers={{ code: CodeBlock, image: Image }}
escapeHtml={ false }
/>
</Col>
</Row>
</div>
);
}
}
// Markdown renderer for images (img tag).
function Image(props) {
return <img alt="" {...props} />
}
export default BlogTemplate;
Blog.jsx
0
1
2
3
4
render() {
return (
<Button className="buttonRead" tag={Link} to="/posts/start_of_a_journey" >Read</Button>
);
}
The web site is deployed on AWS lightsail VM, with an Express.JS server serving the client.
In the VM, I first build the React.JS front end, and then run the Express.JS server to serve the index.html
in the build folder.
index.js
0 1 2 3 4 5 6 7 8 9
const app = express(); app.use(express.static(path.join(__dirname, '..', 'client', 'build'))) app.get('/*', (_, res) => { res.sendFile(path.join(__dirname, '..', 'client', 'build', 'index.html'), (err) => { if (err) { res.status(500).send(err) } }); });
Notice that the GET path is /*
so that when a non base url is reloaded, it won't return 404. For example, when webiste.com/about
web page is reloaded, it won't return 404 anymore.
In designing, developing, ande deploying this web site has taught me many things, and I thoroughly enjoyed it. This will be the first of many journeys.