Skip to content

A developer portfolio and blog built with the React, Next.js, MDX, SCSS, TypeScript.

Notifications You must be signed in to change notification settings

Tymotex/timz.dev

Repository files navigation

Dev Portfolio & Blog

GitHub MIT License GitHub Issues

Deployed Site | Figma (invite-only) | Docs (Storybook)


timz.dev homepage screenshot

A developer portfolio and blog, built with React, Next.js and TypeScript, using MDX for writing blog content and LaTeX for mathematical typesetting.

Uses Storybook for live component documentation. This project uses a GitHub Actions workflow to automatically build and deploy the static assets in docs/ to GitHub Pages.

CI/CD

This project uses the following GitHub Actions workflows:

  • lighthouse-audit – This runs a web vitals audit using Chrome Lighthouse on the deployed instance of the website. It can be configured to run on non-deployed static files. See Lighthouse CI Action.
  • unit-test – This simply invokes yarn test in a container and checks no unit tests are failing.

Content Management

To author new blogs, add a .mdx file to content/blogs/${category} and fill in the following frontmatter format:

---
title: Buffer Overflow Attacks
description: Buffer overflows are when the program tries to write more elements into an array's allocated size.
published: true
date: 2022-06-25
# Optional thumbnail.
thumbnail: /images/thumbnails/cybersecurity.png
# Optional tags to help with categorising blogs.
tags:
    - Cybersecurity
    - Computer Science
    ...
# Optional link to Medium post.
mediumLink: https://...
---

The filename will be used to construct a URL slug.

  • title gets added into the <title> element.
  • description gets written to <meta name="description" content="...">.
  • published indicates whether a blog is publicly viewable.
  • date is the time the blog was published.

The script, scripts/blogs.ts, is used to traverse and source files from the local filesystem to discover .mdx files. The files could be located anywhere, such as AWS S3 bucket, but keeping it all these blog files in the same repo as the project itself is simply more convenient to work with, and you get version control along with it.

Getting MDX to work with the right plugins and build configurations was a really challenging task, especially without much relevant documentation or guidance for it online. Here were some resources I found extremely helpful in building out this system, adapted to my specific:

Blog Source Syntax

The .mdx file can render any of the following:

  • Anything considered valid markdown syntax.
  • Syntax-highlighted code blocks started with the markdown character sequence, ```.
    • This relies on the packages: remark-prism, prismjs, and @types/remark-prism.
  • Embedded LaTeX expressions. You can write them inline or as a separate block.
    • For example:
          This renders an inline LaTeX expression.
          $\alpha$  
      
          This renders a block-level LaTeX expression.
          $$
              \frac{1}{2}
          $$
    • This relies on the packages: remark-math, rehype-katex, katex.
    • See the KaTeX documentation for a list of all LaTeX expressions that are supported.
  • Any custom react components available in the project, ie. any JSX. By extension, this means that you can use any HTML element you want, directly within the .mdx source code.
    • For example:
          import SampleComponent from 'src/components/SampleComponent';
      
          This is a sentence that contains *valid markdown* followed by a 
          custom React component.
      
          <SampleComponent />

All of this is possible through mdx-bundler which is used by the server-side Node.js script, scripts/blogs.ts, to map all the .mdx source code to executable javascript that renders the content.

Contact Form

The contact form was built using the Getform email receipt and delivery service. I followed this guide for setting up the form. The API endpoint string is set inside constants/external.ts and need not be secret.

Issues & Performance

Challenges

  • mdx-bundler doesn't have built-in support for Sass modules. As a workaround, this project uses a global SCSS file at src/blog-components/global.scss.
  • Cannot import next/image in .mdx files when using mdx-bundler. This is really unfortunate because next/image contains a lot of image optimisations that improve the Core Web Vitals. The current workaround is to have a custom component BlogImage that provides styling and options to minimise cumulative layout shift. Eventually, a script could be set up to run prior to yarn build that runs an web image optimiser (converting .png to .webp, for example) on all image files under public/.

Performance & SEO

I developed this site with SEO in mind for the blog pages, however I relaxed this requirement for the portfolio pages which need not load faster.

To optimise bundle size and/or minimise CPU consider cutting or substituting away from the following:

  • framer-motion is only used for page transitions and for polishing UI interactions. The main import, motion adds about 25kb to the bundle size. See reducing Framer Motion's bundle size.
  • tsparticles and react-tsparticles are the main contributors to poorer load performance. These are the main heavyweight dependencies, however they don't impact web vitals by a concerning amount (for a portfolio/blog website).

Caveats

A list of things that helped keep the bundle size lower and improve performance but which were not clearly documented or obvious.

  • Importing icons from react-icons with the most deeply qualified paths for correct tree-shaking. See this issue.
  • Reduce fps limit to <= 30 in tsparticles options. See this post.

About

A developer portfolio and blog built with the React, Next.js, MDX, SCSS, TypeScript.

Resources

Stars

Watchers

Forks