Post Data
{
  "title": "Ground Up",
  "date": "2023-07-25T18:27:24.604Z",
  "slug": "ground-up",
  "author": {
    "name": "Lucas Steinmacher"
  },
  "content": "<h2>Adding a Personal Touch</h2>\n<p>In the waning weeks of June 2023, a simple yet compelling idea took root:\nbreathing new life into my portfolio. The aging Hugo template-generated site\nthat had served me well needed a contemporary touch—a reflection of who I've\nbecome. With an added twist this time—a blog. Since I'm adding the blog I\nthought I'd talk about how it all went down—the ups, the downs, and the \"oh, I\nprobably shouldn't have done that\" moments. No time to waste, let's dive right\ninto the story.</p>\n<h2>Navigating Remix Challenges and Pivot</h2>\n<p>I've been working for a company on a contract for about a year, working on a\nproject based in the Remix ecosystem. When things had slowed down and my\ncontract was coming to an end, I decided to update my portfolio with some of the\nskills that I learned and realized that yeah, the old Hugo needed to go… pun\nmaybe intended. I knew I wanted to build and learn as opposed to just doing\nsomething simple with a template, and since I had already been learning Remix, I\ndecided to give it a shot! I loaded up a new app, found the MDX bundler written\nby Kent C Dodds, and started on my way! I immediately started running into\nissues with it, mainly from the outdated version of Remix that the MDX bundler\nwas based on and the newer version of Remix that I was building with. I put in\nabout three hours into that project and decided to pivot and switch direction.\nNot to be deterred from my intention of building something cool and learning\nalong the way, I opened up my horizons.</p>\n<h2>Building with Next and the T3 Stack</h2>\n<p>The company that I previously worked for, Skilled Recordings, used Next.js for\nthe majority of their applications that they produced, so I decided to go down\nthat direction. I decided to use the T3 stack for its “simplicity, modularity,\nand type safety across the full stack”. I had also come across the packages Gray\nMatter and Unified for parsing Markdown into HTML that I wanted to use. After\ninstalling everything, after about 3 hours I had my Markdown parser setup and\nblog posts ready to go. I was using Tailwind, of course, for styling, TRPC for\nmy API, and Next Auth with Discord to authorize my users. All said and done, a\npretty quick start to get a prototype up of the project and no major bugs to try\nand work around!</p>\n<blockquote>\n<p>The Markdown is parsed using unified and remark</p>\n</blockquote>\n<pre><code class=\"hljs language-ts\"><span class=\"hljs-keyword\">import</span> remarkHtml <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'remark-html'</span>\n<span class=\"hljs-keyword\">import</span> remarkParse <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'remark-parse/lib'</span>\n<span class=\"hljs-keyword\">import</span> { <span class=\"hljs-keyword\">type</span> <span class=\"hljs-title class_\">Plugin</span>, unified } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'unified'</span>\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">default</span> <span class=\"hljs-keyword\">async</span> <span class=\"hljs-keyword\">function</span> <span class=\"hljs-title function_\">markdownToHtml</span>(<span class=\"hljs-params\">markdown: <span class=\"hljs-built_in\">string</span></span>) {\n  <span class=\"hljs-keyword\">const</span> result = <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title function_\">unified</span>()\n    .<span class=\"hljs-title function_\">use</span>(remarkParse <span class=\"hljs-keyword\">as</span> <span class=\"hljs-title class_\">Plugin</span>)\n    .<span class=\"hljs-title function_\">use</span>(remarkHtml <span class=\"hljs-keyword\">as</span> <span class=\"hljs-title class_\">Plugin</span>)\n    .<span class=\"hljs-title function_\">process</span>(markdown)\n  <span class=\"hljs-keyword\">return</span> result.<span class=\"hljs-title function_\">toString</span>()\n</code></pre>\n<blockquote>\n<p>Then the Markdown is simply rendered in a div using dangerouslySetInnerHTML\nand styles are applied from a css module</p>\n</blockquote>\n<pre><code class=\"hljs language-typescript\"><span class=\"hljs-keyword\">import</span> markdownStyles <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">'./markdown-styles.module.css'</span>;\n\n<span class=\"hljs-keyword\">type</span> <span class=\"hljs-title class_\">Props</span> = {\n  <span class=\"hljs-attr\">content</span>: <span class=\"hljs-built_in\">string</span>;\n};\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">default</span> <span class=\"hljs-keyword\">function</span> <span class=\"hljs-title function_\">PostBody</span>(<span class=\"hljs-params\">{ content }: Props</span>) {\n  <span class=\"hljs-keyword\">return</span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&#x3C;<span class=\"hljs-name\">div</span>\n      <span class=\"hljs-attr\">className</span>=<span class=\"hljs-string\">{markdownStyles[</span>'<span class=\"hljs-attr\">markdown</span>']}\n      <span class=\"hljs-attr\">role</span>=<span class=\"hljs-string\">\"article\"</span>\n      <span class=\"hljs-attr\">aria-label</span>=<span class=\"hljs-string\">\"Post Content\"</span>\n      <span class=\"hljs-attr\">dangerouslySetInnerHTML</span>=<span class=\"hljs-string\">{{</span> <span class=\"hljs-attr\">__html:</span> <span class=\"hljs-attr\">content</span> }}\n    /></span></span>\n  );\n}\n</code></pre>\n<blockquote>\n<p>Then finally I fetch the postData content in the [slug].tsx page and parse it\nin getStaticProps then render it in the PostBody component.</p>\n</blockquote>\n<pre><code class=\"hljs language-ts\"><span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">const</span> <span class=\"hljs-title function_\">getStaticProps</span> = <span class=\"hljs-keyword\">async</span> (<span class=\"hljs-params\">{ params }: Params</span>) => {\n  <span class=\"hljs-keyword\">const</span> postData = <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title function_\">getPostBySlug</span>(params.<span class=\"hljs-property\">slug</span>);\n  <span class=\"hljs-keyword\">const</span> parsedContent = <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title function_\">markdownToHtml</span>(postData.<span class=\"hljs-property\">content</span> || <span class=\"hljs-string\">''</span>);\n\n  <span class=\"hljs-keyword\">return</span> {\n    <span class=\"hljs-attr\">props</span>: {\n      <span class=\"hljs-attr\">post</span>: {\n        <span class=\"hljs-attr\">content</span>: parsedContent,\n      },\n    },\n  };\n};\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">default</span> <span class=\"hljs-keyword\">function</span> <span class=\"hljs-title function_\">Post</span>(<span class=\"hljs-params\">{ post, stats }: Props</span>) {\n  <span class=\"hljs-comment\">// [slug].ts logic here...</span>\n  <span class=\"hljs-keyword\">return</span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&#x3C;></span>\n    // XML here\n      <span class=\"hljs-tag\">&#x3C;<span class=\"hljs-name\">PostBody</span> <span class=\"hljs-attr\">content</span>=<span class=\"hljs-string\">{post.content}</span> /></span>\n    // the rest of the XML\n    <span class=\"hljs-tag\">&#x3C;></span>\n  )\n}\n</span></code></pre>\n<h2>The Comment Conundrum</h2>\n<p>After prototyping and sitting on what I was going to do with the authorization\nthat the T3 stack afforded me, I decided to add a comment section, thus making\none of the quintessential mistakes as a software engineer, thinking to myself,\n“Ahh, this will probably just take me a day or two…” Three weeks later, I was\nfinished with the feature! Completed with authorization through Discord, rate\nlimiting on the front and back end, Google reCAPTCHA V2 to make sure that bots\ndon't spam my comment section, language moderation using the Bad Words NPM\npackage (which asterisks out words on the bad word list), and setting up a\nPostgreSQL database hosted on Vercel! Well, to be fair, I did get married during\nthe three weeks and took a week off.</p>\n<h2>Conclusion</h2>\n<p>All in all, things went rather smoothly and I'm excited to make the domain name\nswitch from the old Hugo to this version that you're reading this on when I\ndeploy this blog post. There's still a lot of work to be done and features that\nI'd like to implement. For instance, I'm new to TRPC and some of my backend\ncalls with state changes are kind of ugly. I'd like to clean them up and\nabstract them into their own utility files, as well as implement a language\nchange using Langchain and connecting to ChatGPT for instance, as well as on the\ncomment section. Oh and I'm working on custom syntax highlighting for code\nblocks in the markdown, I'll update this when I get the css figured out. I'll\nalso be making bi-weekly blog posts if not monthly blog posts on what I've been\nup to, but until then, I hope you enjoy!</p>\n<h2>Update October 2023</h2>\n<p>I finished adding syntax highlighting for code blocks using\n<a href=\"https://github.com/rehypejs/rehype-highlight\">rehype-highlight</a>. The package is\nin the Unified ecosystem that I didn't find before and works seamlessly to add\ncss classes to the elements in the <code>&#x3C;pre></code> so that you may add custom css for\nthe code blocks. I mimicked the\n<a href=\"https://github.com/catppuccin/catppuccin\">Catppuccin</a> Macchiato theme color\nscheme for the highlighting. Hopefully a little easier on your eyes. Tackling\nlight mode is next.</p>",
  "updatedAt": "2023-10-25T02:21:25.157Z"
}

Ground Up

2023-07-25

5 min read

Written by: Lucas Steinmacher

Updated on: 2023-10-25 *

Adding a Personal Touch

In the waning weeks of June 2023, a simple yet compelling idea took root: breathing new life into my portfolio. The aging Hugo template-generated site that had served me well needed a contemporary touch—a reflection of who I've become. With an added twist this time—a blog. Since I'm adding the blog I thought I'd talk about how it all went down—the ups, the downs, and the "oh, I probably shouldn't have done that" moments. No time to waste, let's dive right into the story.

Navigating Remix Challenges and Pivot

I've been working for a company on a contract for about a year, working on a project based in the Remix ecosystem. When things had slowed down and my contract was coming to an end, I decided to update my portfolio with some of the skills that I learned and realized that yeah, the old Hugo needed to go… pun maybe intended. I knew I wanted to build and learn as opposed to just doing something simple with a template, and since I had already been learning Remix, I decided to give it a shot! I loaded up a new app, found the MDX bundler written by Kent C Dodds, and started on my way! I immediately started running into issues with it, mainly from the outdated version of Remix that the MDX bundler was based on and the newer version of Remix that I was building with. I put in about three hours into that project and decided to pivot and switch direction. Not to be deterred from my intention of building something cool and learning along the way, I opened up my horizons.

Building with Next and the T3 Stack

The company that I previously worked for, Skilled Recordings, used Next.js for the majority of their applications that they produced, so I decided to go down that direction. I decided to use the T3 stack for its “simplicity, modularity, and type safety across the full stack”. I had also come across the packages Gray Matter and Unified for parsing Markdown into HTML that I wanted to use. After installing everything, after about 3 hours I had my Markdown parser setup and blog posts ready to go. I was using Tailwind, of course, for styling, TRPC for my API, and Next Auth with Discord to authorize my users. All said and done, a pretty quick start to get a prototype up of the project and no major bugs to try and work around!

The Markdown is parsed using unified and remark

import remarkHtml from 'remark-html'
import remarkParse from 'remark-parse/lib'
import { type Plugin, unified } from 'unified'

export default async function markdownToHtml(markdown: string) {
  const result = await unified()
    .use(remarkParse as Plugin)
    .use(remarkHtml as Plugin)
    .process(markdown)
  return result.toString()

Then the Markdown is simply rendered in a div using dangerouslySetInnerHTML and styles are applied from a css module

import markdownStyles from './markdown-styles.module.css';

type Props = {
  content: string;
};

export default function PostBody({ content }: Props) {
  return (
    <div
      className={markdownStyles['markdown']}
      role="article"
      aria-label="Post Content"
      dangerouslySetInnerHTML={{ __html: content }}
    />
  );
}

Then finally I fetch the postData content in the [slug].tsx page and parse it in getStaticProps then render it in the PostBody component.

export const getStaticProps = async ({ params }: Params) => {
  const postData = await getPostBySlug(params.slug);
  const parsedContent = await markdownToHtml(postData.content || '');

  return {
    props: {
      post: {
        content: parsedContent,
      },
    },
  };
};

export default function Post({ post, stats }: Props) {
  // [slug].ts logic here...
  return (
    <>
    // XML here
      <PostBody content={post.content} />
    // the rest of the XML
    <>
  )
}

The Comment Conundrum

After prototyping and sitting on what I was going to do with the authorization that the T3 stack afforded me, I decided to add a comment section, thus making one of the quintessential mistakes as a software engineer, thinking to myself, “Ahh, this will probably just take me a day or two…” Three weeks later, I was finished with the feature! Completed with authorization through Discord, rate limiting on the front and back end, Google reCAPTCHA V2 to make sure that bots don't spam my comment section, language moderation using the Bad Words NPM package (which asterisks out words on the bad word list), and setting up a PostgreSQL database hosted on Vercel! Well, to be fair, I did get married during the three weeks and took a week off.

Conclusion

All in all, things went rather smoothly and I'm excited to make the domain name switch from the old Hugo to this version that you're reading this on when I deploy this blog post. There's still a lot of work to be done and features that I'd like to implement. For instance, I'm new to TRPC and some of my backend calls with state changes are kind of ugly. I'd like to clean them up and abstract them into their own utility files, as well as implement a language change using Langchain and connecting to ChatGPT for instance, as well as on the comment section. Oh and I'm working on custom syntax highlighting for code blocks in the markdown, I'll update this when I get the css figured out. I'll also be making bi-weekly blog posts if not monthly blog posts on what I've been up to, but until then, I hope you enjoy!

Update October 2023

I finished adding syntax highlighting for code blocks using rehype-highlight. The package is in the Unified ecosystem that I didn't find before and works seamlessly to add css classes to the elements in the <pre> so that you may add custom css for the code blocks. I mimicked the Catppuccin Macchiato theme color scheme for the highlighting. Hopefully a little easier on your eyes. Tackling light mode is next.

Back to blogs →

Have an opinion of what I said? Find a typo? Just want to be nice?
Feel free to leave a comment!

You must be logged in to leave a comment.

Comments