Building This Blog Pt. 2
Learning Remix
First things first, Remix has a cli tool to get an initial project up and running. Since this is my first time using it, I'm very appreciative.
I like Netlify for deploying and hosting, especially since they have a super generous free tier. The other selection is TypeScript, because hell yeah TypeScript. I said no to the npm install since I prefer yarn.
We've got a basic setup and a README with instructions for running it and deploying to Netlify, as we picked. Cool! We can run yarn
to install all our dependencies, then yarn dev
to start up the development server and see what we have.
Basic page with a few links to the Remix docs, cool cool. Let's see what files we got and how exactly this is displaying.
Most of that is regular node/TypeScript bs, along with some configs for Remix and Netlify Functions. Let's check out this root.tsx, shall we?
Quite a few components imported in, along with our basic HTML tags to make a page. This page and this other page of the docs explains what these are doing. The TL;DR is they let you declare meta tags and links and such in any of your pages/components and this will round them up and put them in the right place.
As for the site itself, this is also where the layout will end up going. Things that are going to be on every page, like the navigation and such. Outlet where the inner pages will render, so we just make sure to put that as a child of our layout, and we're good to go!
✨ Styling ✨
I loooooove Tailwind, and will very likely be writing a future post about it and why I like it. For now, I'll say it takes care of a lot of the hassle of CSS without enforcing anything. Tailwind has a walkthrough on setting it up with Remix, so let's just follow that! Essentially it's just installing Tailwind and stuff to process it alongside Remix, with the same commands.
This does change up the scripts in our package.json, and their walkthrough doesn't assume we're working with Netlify, so it takes a bit of editing to make it all good.
"scripts": {
"build": "npm run build:css && remix build",
"build:css": "tailwindcss -m -i ./styles/app.css -o app/styles/app.css",
"dev": "cross-env NODE_ENV=development concurrently \"npm run dev:css\" \"netlify dev\"",
"dev:css": "tailwindcss -w -i ./styles/app.css -o app/styles/app.css",
"start": "cross-env NODE_ENV=production netlify dev"
}
With Tailwind working, we can get to making it look all fancy
Building The Layout
Remix has a Link component, which is basically an a tag with some prefetching and things for React Router, so we'll use that to build out the navigation for the site. Even better, it has a NavLink component that knows if it's active, so we can be all fancy with styling the active link. I'm going to have Home, Blog, Contact, and Projects. (Projects will have writing, games, and tech stuff that you can filter between, but that's for later)
If you've used something like Next or Gatsby or static site generators, Remix functions in a similar way when it comes to the pages of the site. You have your app directory, within that a routes directory, and each file in that becomes a page that you can link to and visit. It's why the meta and link functions are so interesting, by having those in the root, you can just export a meta function in each route that sets whatever you need on the page, and it's all handled and easy.
My layout is pretty straight forward. A nav bar at the top for the pages and a lil logo, with content for each below that. The <Outlet />
component in the root is what renders each page for that particular route, and of course you can have multiple layers of that for subpages. The blog route will have subpages but won't actually use an Outlet, since each post is just its own thing. For now the rest will be simple, just a single page.
Building The Blog
Okay, for the blog page we get to use some of the fun stuff Remix provides. Here's a pared down look at the root of the /blog
route.
The "backend" and "frontend" are all in the same file. When the route is hit, it calls that LoaderFunction. Any data/props you need are fetched there, and the json function returns it as a regular json response. The component below can use that data with the useLoaderData
hook, giving you whatever the loader function returned. For this, I'm grabbing all the blog posts, their id, slug, and title, and showing a link to each. More details about how it's grabbing this will be in the part 3 that looks more into using GraphCMS.
Each blog post page does something similar, just grabbing the particular post and using that data to display it. We get to use the meta function with this! That function lets us set whatever data we want for all the fancy shmancy SEO and metadata and page title. It can also use things from the loader function, so we can use the blog title as the page title! Science rules!
For the projects/portfolio side, it will basically work the same, just with different data. I'll make another group of content in GraphCMS, and on the projects page, the code will look very similar to the blog page. A loader function to pull the data, then in the component using that pulled data and displaying it.
ERROR ERROR ERROR
But what if you go to a page that isn't found? Or what if GraphCMS is down and there's no data for the post to show? Remix is super clever with error handling. It can catch them and use the nearest error boundary when that happens. "Nearest" meaning the one it finds first, starting with the particular page, then any pages it's inside of, then finally the root file. This is fun and handy because a subpage erroring doesn't mean the root or any parent pages will break, it'll just render the error boundary inside of them.
For this particular site, I think the only pages that need a specific error boundary are things pulling from GraphCMS. That's the blogs and the projects pages. Everything else can just be caught by the root file, if they happen to error out. The root file still needs to render all the HTML stuff though, so all the meta and links and such can be a wrapper around the main app and the error boundary, like this
So let's say I do something silly like throwing an error in the Projects component. With that error boundary on root, it's fine! We see this
How ✨fancy✨! Even fancier, this ErrorBoundary is for issues we don't really expect, server responses with 500 codes and things like that. For things we can anticipate, like trying to hit a blog post that doesn't exist, Remix gives us another tool called a Catch Boundary. It's basically the same thing as the ErrorBoundary in a lot of ways, but it gets a special hook for the rendered component to show what happened. In these cases, the backend would throw some kind of error response, like a 404. In the Catch Boundary we can use the hook to see that and handle it how we like.
The easiest way to distinguish is that Catch Boundary is for things we know can happen, anticipate them, and handle them properly. Error Boundary is for everything else! Stuff we didn't anticipate, the "uhhhhhh that's not supposed to happen" situations where most sites or programs just go "An Error Occured" because they don't know what to do. The boundaries let us at least give the user some way of continuing. The Catch Boundary lets us give relevant paths forward. If a blog post wasn't found and we get that 404, we could say "Hey that post doesn't exist but maybe you want to see the latest post instead." ✨user experience✨
Conclusion
From here it's filling this with content. Remix is a really cool system. There's sooooo many frameworks and systems and tools for developing, especially web apps and sites. The ones that I enjoy using give you tools to make things without getting in the way. I feel like Remix does that. It gives options and methods but doesn't force into a specific way that has to be customized if you want to do anything outside of the specific way it does things.
For part 3, we'll be going over GraphCMS, making content in it and puling it to the app itself.