How I Built My Personal Website
Published: 2021-01-22 23:05:15

I had a lot of fun building my personal site, because I used it as an opportunity to learn some new tech. Let's take a tour of what's in it.

# Core

At it's core, the site is powered by [Next.js](https://nextjs.org/) and [React](https://reactjs.org/). I used Next.js because the content is mostly static, which is a use-case Next excels at. Next enables Server Side Rendering (SSR) by default, but also supports Server Side Generation (SSG), which generates static HTML pages at build time. You can read more about the differences between SSR and SSG in [this great article by the Vercel team](https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation).

{% github vercel/next.js no-readme %}

## Code Style

In the project, I used React functional components with hooks *almost* exclusively because I find I can write much cleaner and more concise components using the hooks API vs. traditional class-based components; the only exceptions being [`_app.tsx`](https://nextjs.org/docs/advanced-features/custom-app) and [`_document.tsx`](https://nextjs.org/docs/advanced-features/custom-document), which extend base classes from the Next framework. A `_document.tsx` file allows you to customize the base HTML template that the React app is injected into, while `_app.tsx` allows you to use shared layouts, add an [Error Boundary](https://reactjs.org/docs/error-boundaries.html), import global stylesheets, and more.

## Build Customization

### Markdown Copy Content

I've also added [`@mdx-js/loader`](https://www.npmjs.com/package/@mdx-js/loader) and [`@next/mdx`](https://www.npmjs.com/package/@next/mdx) to my build configuration, which allows me to import markdown (*.md) files as React components. Using this technique allows me to keep some of my copy text (such as the "about" page) in markdown rather than having to manually write out the JSX for it, which is just simpler and easier to maintain.

### Working with SVGs

I'm using [SVGR](https://github.com/gregberge/svgr) to transform my SVG files into React components, rather than loading them via `<img src="/my-image.svg"/>`. This means my SVGs will be loaded with the markup, rather than being loaded asynchronously, which can help eliminate [Cumulative Layout Shift (CLS)](https://web.dev/cls/) issues. It's as simple as customizing your `next.config.js` file to use the `@svgr/webpack` loader for `*.svg` file types, then importing them in your components, a la:

```typescript
import SvgAvatar from "/avatar.svg";

const MyComponent: React.FC = () => (
  <MyComponent>
    <SvgAvatar/>
  </MyComponent>
);
```

### DEV Blog

I previously wrote another article about how we can use the DEV API to load your articles in order to embed them in your own website.

{% link matjones/how-to-embed-your-dev-to-blog-in-your-personal-website-4l81 %}

I combined this technique with Next's [`getStaticProps`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) method to load all my DEV articles at build time, and statically generate the blog pages for my site. To keep the pages up-to-date, I configured the [GitHub Action](https://github.com/features/actions) that builds and deploys my site to run periodically. I [configured it to run every 30 minutes](https://github.com/mrjones2014/mrjones2014.github.io/blob/master/.github/workflows/integrate.yml), however in practice it seems like for some reason it actually runs closer to once every hour. If your CI/CD pipeline is actually time-sensitive, I recommend not using GitHub Actions.

## Design

## UI Framework

On a recommendation from a friend, I decided to learn [Blueprint.js](https://blueprintjs.com/) and use it to build my website. After building lots of components using it, tweaking it, and styling it, I think it has become my new favorite React UI framework.

Right out of the box, everything looks amazing, cohesive, and enterprise-quality. Their component library is pretty extensive, and includes just about everything you'd want in your base toolkit, from simple stuff like [navbars](https://blueprintjs.com/docs/#core/components/navbar) and [buttons](https://blueprintjs.com/docs/#core/components/button), to layout things like [cards](https://blueprintjs.com/docs/#core/components/card), to more complex components like [interactive menus](https://blueprintjs.com/docs/#core/components/menu), [progress bars](https://blueprintjs.com/docs/#core/components/progress-bar) and [drawers](https://blueprintjs.com/docs/#core/components/drawer). It's pretty easy to customize and compose the built-in components to make more complex designs. My only complaint is that there are a select few places I had to use the [dreaded `!important` CSS directive](https://uxengineer.com/css-specificity-avoid-important-css/#why-avoid-!important) to override some specific styles from Blueprint's stylesheets.

## Stylesheets

My go-to language for stylesheets has been [SCSS](https://sass-lang.com/) for a long time, because, well, let's face it, [writing vanilla CSS just sucks](https://hackernoon.com/why-you-shouldnt-write-vanilla-v92333xn).

One of the greatest features of CSS preprocessors like Sass is the [ability to write nested styles](https://sass-lang.com/documentation/style-rules#nesting); if your markup can be nested, then why can't your stylesheets?!

You can also easily re-use bits of common styles via [mixins](https://sass-lang.com/documentation/at-rules/mixin), and even run compile-time [logical operations](https://sass-lang.com/documentation/operators) to further customize the generated stylesheets. It's incredibly powerful and extensible.

## Deployment

My site is hosted statically on [GitHub Pages](https://pages.github.com/), configured to deploy from the `gh-pages` branch of the repository. Deployment happens automatically on push, periodically, and can be manually triggered via a [GitHub Actions workflow](https://github.com/mrjones2014/mrjones2014.github.io/blob/master/.github/workflows/integrate.yml). The date of the most recent build is injected into the app at compile time via an environment variable in `next.config.js`.