~~ ~ ~ ~~~ ~ ~ (○) ~ (○) │ └ ]┘ ─ ¬⌐ /VVV\ /V \ │ │ \∩∩∩∩∩/ ___
full-stack web dev
and more
~>projects
~>linkedin
~>github
This is a static site powered by Remix, Tailwind, and MDX. It's hosted on GitHub Pages and uses GitHub Actions to automatically deploy the site when I push up changes.
I learned Remix and made this site in a week! The first iteration, anyway. The source code is publically available; feel free to use the project as a starting point for your own site.
I wanted to try out Remix for this project. Remix is great for server-side rendering (aka SSR), but this site is totally static, so it's not really needed. I wanted to generate all the pages at build time (static site generation aka SSG) so I can host the site for free forever using a static host like GitHub Pages.
(note: there are platforms out there that offer free Remix hosting, but I had my heart set on building to a static site.)
I found an excellent script written by Habib Hinn (detailed in this blog post) to generate the pages, inspired by a post and demo project by Michael Jackson, one of the co-founders of Remix. At a high level, here's how it works:
I updated the build
command in package.json
so that building the whole site is as easy as running npm run build
. The resulting ./build/client
directory is ready to deploy. But first I need some content... 🤔
I decided to write my page content with MDX which is effectively Markdown plus React. I like Markdown because it's easy to write — it's got decently expressive formatting options with a minimal syntax. With MDX, in addition to the standard Markdown features, I can also inject React JSX or HTML when I need something more custom or fancy on a page. This very page you're reading was written as an MDX file.
Thanks to the flexibility of Remix, I could also write JSX/TSX instead of MDX on a specific page if I want just by changing the file extension. For example:
app/
└── routes/
├── my-first-post.mdx
├── my-second-post.jsx
└── my-third-post.tsx
Anyway, I added the MDX Rollup plugin and configured it by following these instructions in the Remix docs. I also had to define some styles for the HTML elements emitted by the MDX plugin since Tailwind overrides the defaults for those elements.
As Michael notes in his demo project, all links need to do a full page reload since there's no server running to handle client-side page transitions. He mentions using <Link reloadDocument>
but I use <a>
tags because I don't need to leverage the client-side router at all. The MDX plugin already emits <a>
tags for links, so I only need to remember this when I'm writing a JSX or TSX page.
I've used GitHub Pages a bunch of times for super easy static hosting so I'm using it again here. I already had a public repo on GitHub for this project, so I went into the settings and configured my project to deploy from the root of the gh-pages
branch. I built the static site locally, pushed it onto this branch, and voilà! My site was live 🙌
This works as-is, but I don't want to have to remember to manually build and deploy the site every time I update a post or something. Thanks to GitHub Actions, I can define a simple script (aka a CI/CD pipeline) to automatically build and deploy the site when I push up commits on the main
branch. I added a workflow based on the one detailed in this article by Daniel Jimenez Garcia. It's pretty straightforward:
npm run build
command configured before).gh-pages
branch.Note that because the gh-pages
branch is getting a brand-new repo force-pushed onto it on every deploy, the branch is getting completely overwritten every time. I'm ok with that because I don't care about preserving the history of this branch. Thus I won't make any changes to this branch manually; if I want to make a change to the deployed site, it has to be in the project files, the build process, or the host config.
Finally, I bought my custom domain and configured it for my site by following the GitHub docs. I also enforced HTTPS and verified my domain for additional security.
Also, importantly, there are two files that need to exist in the root of the deploy source (gh-pages
branch): CNAME
and .nojekyll
.
CNAME
file in the root of the deploy source which is used for routing..nojekyll
file to the root of the deploy source to prevent Jekyll from getting involved.I added both of these files to the public
directory in my project — that way, when the deploy action is triggered and it overwrites the contents of the gh-pages
branch, the files are always copied into the deploy source.
That's how I set up this site! I'm happy with how it works so far and I have ideas for improvements, so keep an eye out for more posts. Hope this was useful in some way. Peace ✌