<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dominic Magnifico</title>
    <description>The latest articles on DEV Community by Dominic Magnifico (@magnificode).</description>
    <link>https://dev.to/magnificode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F127422%2Fd3129eb2-a6f7-4353-a747-b79776e0dc95.png</url>
      <title>DEV Community: Dominic Magnifico</title>
      <link>https://dev.to/magnificode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/magnificode"/>
    <language>en</language>
    <item>
      <title>The Struggle Is What We Crave</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Wed, 01 Apr 2026 16:56:42 +0000</pubDate>
      <link>https://dev.to/magnificode/the-struggle-is-what-we-crave-i7h</link>
      <guid>https://dev.to/magnificode/the-struggle-is-what-we-crave-i7h</guid>
      <description>&lt;h2&gt;
  
  
  The confession
&lt;/h2&gt;

&lt;p&gt;I've been working a lot more lately. Way more than I used to. I've had a pretty solid work life balance in the past that I kind of prided myself on. After 7pm was my time. My kid was in bed (or should have been), Slack was shut down, and the client work that was on my mind took a back seat. But something changed, or maybe several things all at the same time kind of. But the first thing to change was my desire to be a part of something bigger than myself. It's a cliche or whatever, but every year that goes by I see my son become more and more enthralled with the world around him. He notices shit, remembers that time I said something off-hand. Point being, I want to make sure that he sees how important it is to be a part of something bigger than himself in a world that's becoming more isolated day after day.&lt;/p&gt;

&lt;p&gt;There's a million different variations of isolationism that are taking hold in the world today, and the one I'm going to be talking about is probably the least impactful of all of them. But it's the one right in front of me. The one I've been a part of for more than 15 years, and it's the isolation of the web. It feels like an oxymoron to say, I mean, the web was built to connect the world. There was some god damn inspiring stuff that happened between the days of Ada Lovelace and the .com boom in the 90s. Stories of nerds coming together to make the component parts of computers more accessible to normal folks. The Tim Berners-Lee's of the world who pushed for a standardized way of sharing and linking documents. Some real populist shit.&lt;/p&gt;

&lt;p&gt;When I was coming up in the web world, the community was the thing that made it so appealing. There were so many meetups, conferences, workshops and the like. And they were centered around some of the core pieces of what building on the web are. I remember going to the CSS Dev Conf in Estes Park in 2013 and being blown away by the breadth of knowledge being shared. The whole conference was focused on CSS. Chris Coyier gave the opening keynote, there were a couple talks on Sass and Compass that were so cutting edge and cool. Tab Atkins even spoke on the future of CSS. It was so rad to see a group of folks I looked up to coming together to talk about what I thought was the best part of web development at the time.&lt;/p&gt;

&lt;p&gt;It feels like the COVID days really fucked up the momentum that tech conferences like this had, and I imagine it was a nightmare trying to manage one of these things anyway. Skip a year of funding or ticket sales or sponsors or whatever, and then pair that with the uncertainty of a conference ever happening again, and I think the switch just kind of flipped off.&lt;/p&gt;

&lt;p&gt;I read a book a long time ago called "The Sixth Extinction" by Elizabeth Kolbert that outlined the five previous mass extinction level events that had occurred over the past half billion years, and then explored and identified that we were the catalyst in the current ongoing sixth extinction. I think web development has gone through several extinction level events too. And the latest one we're witnessing is AI.&lt;/p&gt;

&lt;p&gt;It's tough to see it from the inside. It's even tougher to see it when you're so blinded by the productivity gains you’re seeing in your day to day work. But the deeper I dive into it, the clearer it becomes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The thing I loved about this industry is dying, and I'm watching it happen from the inside.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reason We Found Each Other
&lt;/h2&gt;

&lt;p&gt;Alright. You're into my line of thought at this point, so follow me here.&lt;/p&gt;

&lt;p&gt;Why did conferences like CSS Dev Conf exist in the first place? They happened because CSS was hard. If you lived through those days you'll remember that floats were annoying as hell, vertically centering a div was a meme, and figuring out how to do responsive design was a totally foreign concept (if you didn't live through those days consider yourself very lucky). We &lt;strong&gt;needed&lt;/strong&gt; these communities so that we could leverage someone else's hard won knowledge. The struggle is what created the community.&lt;/p&gt;

&lt;p&gt;We have way less of that now.&lt;/p&gt;

&lt;p&gt;Things like JSConf, and CSSConf don't really exist anymore. That domain knowledge has effectively been abstracted away by the ability to just let Claude do it for you, without understanding the fundamentals. Take it a step further and look at StackOverflow. I can't tell you how many times I'd leveraged SO to make it look like I knew what I was talking about early on in my career. They went from &lt;a href="https://blog.pragmaticengineer.com/are-llms-making-stackoverflow-irrelevant/" rel="noopener noreferrer"&gt;200,000 new questions a month to under 50,000&lt;/a&gt; in three years. Nobody's asking questions (at least publicly) anymore. And that's a fucking problem.&lt;/p&gt;

&lt;p&gt;The struggle of sifting through several Stack Overflow threads, trying one piece of code, mashing it up with another from a comment that got fewer upvotes, and seeing how that affected your specific problem was a pretty amazing way to learn lots of different concepts. And the Stack Overflow Gods that provided extremely detailed responses that linked out to MDN docs, or the spec itself were better teachers than half the ones I had getting my bachelors in "Web Development and Interactive Media" in 2010.&lt;/p&gt;

&lt;p&gt;These cornerstone pieces of the web were some the first casualties in the web's "sixth extinction". The next domino to fall, which we're already seeing, is the death of open source contributions.&lt;/p&gt;

&lt;p&gt;The entire open source contribution model is changing. There are so many selfless developers out there who built, or are building open source tools to give back to the same community that I came up loving, and are getting absolutely inundated with AI slop contributions because people see those GitHub "contributed to" lists as some kind of status symbol. I generalize here obviously, but it's breaking so many good developers trying to sift through all this garbage that some open source projects like &lt;a href="https://github.com/ghostty-org/ghostty/pull/10412" rel="noopener noreferrer"&gt;Ghostty&lt;/a&gt; and &lt;a href="https://github.com/tldraw/tldraw/issues/7695" rel="noopener noreferrer"&gt;tldraw&lt;/a&gt; are having to write aggressive AI usage policies, or are locking the doors completely to prevent people from spamming them with AI slop PRs.&lt;/p&gt;

&lt;p&gt;It gets even more horrifying when you start to realize how truly immense the loss of jobs is going to get. Look at companies like &lt;a href="https://www.fastcompany.com/91500048/in-a-626-word-x-post-jack-dorsey-justifies-his-decision-to-lay-off-40-of-blocks-workforce" rel="noopener noreferrer"&gt;Block&lt;/a&gt;, &lt;a href="https://www.inc.com/leila-sheridan/why-oracle-is-cutting-30000-jobs-despite-a-massive-6-billion-quarterly-income/91325068" rel="noopener noreferrer"&gt;Oracle&lt;/a&gt;, &lt;a href="https://www.forbes.com/sites/martineparis/2025/10/27/amazon-to-cut-14000-jobs-as-microsoft-calls-ai-labor-reducing-tech/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt; and pretty much every other big name. They're laying off workers in droves after posting record profits. I've lived through several of these extinction level events in web development but this one I think has had the most staggering number of layoffs that can be directly tied to it. People in these positions, were literally building the tech the higher ups were going to use to replace them. They were building their own coffins.&lt;/p&gt;

&lt;p&gt;It leads us to the last casualty in this extinction that I'll talk about, and that's the junior developers. Junior developers were having a miserable time finding a job before the AI boom. The industry was doing everything it could to snatch up every senior dev on the block to avoid having to train up the juniors. That created a talent pool of so many people that remains untapped. I can't fathom how much potential here got pushed out and had to find work in a different industry. It's a shame because there was so much wonder around the industry when I first started, and seemingly the 7 month bootcamps really started to suck that wonder out of these juniors, with AI being the final straw that's going to make it even more difficult for junior devs to break into the industry.&lt;/p&gt;

&lt;p&gt;It's depressing to realize, but, the thing that was supposed to help developers write better code is destroying the ecosystem. It was supposed to allow us to focus on bigger issues, and instead is effectively locking down open source projects, making it easier for soulless execs to justify laying off massive swathes of their workforce, and is making it harder for meaningful progress to be made in the effort to bring new, passionate talent into the industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Callouses
&lt;/h2&gt;

&lt;p&gt;I've got a lot of these.&lt;/p&gt;

&lt;p&gt;It's hard to pick one that really illustrates the point. I could tell you about the time I built a multi stream gaming platform that took raw video feeds from Esports competitions, used ffmpeg to encode them, live stream them, and allow users to switch between feeds as the tournament was going on to see different perspectives. The sheer amount of AWS hot garbage documentation I had to read to try to understand how the hell to use MediaEncoder to get the bitrate right so six streams were the right quality. The all nighters that we pulled using test feeds from six Xbox's connected to a server to make sure the demo we were running the next day would work just long enough to sell the product.&lt;/p&gt;

&lt;p&gt;Or I could go into depth as to why the partnership between two really close friends who helped me build &lt;a href="https://houseofgiants.com" rel="noopener noreferrer"&gt;House of Giants&lt;/a&gt; fell apart. These were people I'd had amazing relationships with. I hired one of them at my first agency. We had this grand vision for how we'd run House of Giants. We modeled ourselves as an anti-agency. A buck against the status quo because we absolutely despised how the agencies we worked at in the past were run. Out of touch executives and leadership who would clutch their pearls harder than the clam they stole them from. And it was great for a while, but then the stakes were raised. Our ambitions diverged, the way we wanted to run things, chase work (or not) became an argument rather than a conversation. How we went about learning and expanding our technical knowledge and skillsets turned into arguments about who's job it was to do Task X over task Y.&lt;/p&gt;

&lt;p&gt;The struggles in these moments..those are the things that teach you the most. The ambitions of the project, your uncertainty about how the fuck you're going to execute on the idea. The panic scrolling through Stack Overflow. The unbearable contemplation of how you stop being business partners with friends. That's where the growth happens. It feels paradoxical, especially in the moment, that the struggle during these times is what would end up making you more efficient in the long run. And I think a lot of shops, and even gigantic corporations (looking at you GitHub, with your newly acquired 90% uptime milestone) are going to face the wrath of this in short order.&lt;/p&gt;

&lt;p&gt;I'm not afraid that AI exists. I do client work, and I'm doing that client work faster than I ever have in the past, while simultaneously building more than I've ever built on the side. Taking side projects from an idea I've had for years to production in a week. The tools are getting better, and they're helping me do more.&lt;/p&gt;

&lt;p&gt;What I'm worried about is that in five years, the people making the architecture decisions won't have the scar tissue to know what to do when it breaks. They'll patch it with another AI slop generated set of changes that start with "Great catch! We should get that fixed" coming back from their agent.&lt;/p&gt;

&lt;p&gt;I'm worried about an entire generation of developers who are going to build without callouses like these to inform their decisions. That judgment layer, the thing that separates "I can build this" from "should I build this?", is completely disappearing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Paradox of Ease
&lt;/h2&gt;

&lt;p&gt;I keep coming back to something I've written about before. &lt;a href="https://dommagnifi.co/2015-09-25-being-comfortable-with-being-uncomfortable/" rel="noopener noreferrer"&gt;Being comfortable with being uncomfortable&lt;/a&gt;. I think that a lot of humanity is lost when we avoid the struggle. The more comfortable we are with being comfortable, the harder it is for us to push ourselves to do something difficult.&lt;/p&gt;

&lt;p&gt;This is another thing I want my son to know about me, that I wasn't afraid to do something I was scared of. I'm the type of person that puts the spider in your basement in a cup, and takes it outside instead of smashing it. I used to collect the rollie pollies in my parents garage and take them to the grass. See a worm on the sidewalk after it rains? I'll pick you up and put you back in the mud dude, I got you. In total opposition to those convictions I went hunting for the first time last October and killed an elk. Field dressed it (with the help of some very good friends), and packed it out. I had a ton of internal conflict about that. But at the end of the day I wanted to show my kid that there are times in life you have to make a decision that might go against everything you think you're about.&lt;/p&gt;

&lt;p&gt;Now when I ask my kid what his favorite food is he says "elk steak!", and that gives me some sort of weird primal satisfaction knowing what I went through to provide that experience for him. That's a part of his life now, something that was so emotionally taxing for me, will live as a positive experience for him.&lt;/p&gt;

&lt;p&gt;We keep trying to move so fast to build tools that eliminate all the friction in our lives and then sit wondering why nothing feels like it matters anymore. We stop getting together to talk about those hard fought battles, and we scream into the void that is our AI agent, and all that does is reflect back to you all the bullshit you want to hear beneath the subtext of your prompt. I build a ton of stuff with a swarm of AI agents and I still feel this way. That says something.&lt;/p&gt;

</description>
      <category>career</category>
      <category>watercooler</category>
      <category>writing</category>
    </item>
    <item>
      <title>Ten Years Uncomfortable</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 24 Mar 2026 19:36:52 +0000</pubDate>
      <link>https://dev.to/magnificode/ten-years-of-uncomfortable-k9f</link>
      <guid>https://dev.to/magnificode/ten-years-of-uncomfortable-k9f</guid>
      <description>&lt;p&gt;When I was 25 I wrote a blog post about &lt;a href="https://dommagnifi.co/2015-09-25-being-comfortable-with-being-uncomfortable/" rel="noopener noreferrer"&gt;leaving the best job I'd ever had&lt;/a&gt;. Not because it was bad...because it was comfortable. I said something about developers finding comfort in the repetition of a well-written loop. Everything comes out uniform, just how you intended it. And I said it was time to break the loop.&lt;/p&gt;

&lt;p&gt;That was ten years ago.&lt;/p&gt;

&lt;p&gt;I'm 35 now. I have a kid. I run a development studio. I haven't written anything real in a long time.&lt;/p&gt;

&lt;p&gt;Not because I don't have things to say. Because somewhere along the way I convinced myself my opinion doesn't matter anymore. There are louder people. More charismatic people. People who seem to have the time and energy to sit down and craft a take and put it out there. I am not those people. I'm the guy who's trying to find 30 minutes that aren't already claimed by something else.&lt;/p&gt;

&lt;p&gt;But here's what I've been thinking about lately.&lt;/p&gt;

&lt;p&gt;At 25, being uncomfortable meant leaving a job. It was dramatic. It was a clear before and after. You could point to the moment and say "that's when things changed."&lt;/p&gt;

&lt;p&gt;At 35, being uncomfortable is quieter. It's running a studio and quoting $25k/month to clients while wondering if you're worth it. It's turning down projects because they don't meet your bar, even when the money would help. It's building something at 10 PM on a Saturday not because anyone asked you to, but because you still can't stop tinkering. It's knowing you're good at this and still feeling like you haven't earned the right to say it out loud.&lt;/p&gt;

&lt;p&gt;The discomfort at 25 was about leaving. The discomfort at 35 is about staying...staying in the work, staying honest about what you don't know, staying visible when it's easier to just put your head down and build.&lt;/p&gt;

&lt;p&gt;I don't have a framework for this. No five steps. No thread with a fire emoji. Just the observation that the guy who wrote &lt;a href="https://dommagnifi.co/2015-09-25-being-comfortable-with-being-uncomfortable/" rel="noopener noreferrer"&gt;that blog post at 25&lt;/a&gt; was onto something, and the guy at 35 keeps forgetting it.&lt;/p&gt;

&lt;p&gt;Some change is good, as hard as it is to recognize. I learned that once in a big, dramatic swing, and then again in smaller ways through years like &lt;a href="https://dommagnifi.co/2015-12-29-%F0%9F%91%8B-2015/" rel="noopener noreferrer"&gt;2015&lt;/a&gt; and first-time leaps like &lt;a href="https://dommagnifi.co/2015-08-17-what-i-learned-from-my-frist-speaking-gig/" rel="noopener noreferrer"&gt;giving my first meetup talk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote that. Ten years ago. I should probably listen to myself more.&lt;/p&gt;

</description>
      <category>career</category>
      <category>community</category>
    </item>
    <item>
      <title>I Didn't Like Other Workout Tracking Apps, So I Built My Own.</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 27 Jan 2026 21:37:27 +0000</pubDate>
      <link>https://dev.to/magnificode/building-opentrainer-real-time-workout-tracking-with-convex-and-nextjs-59h4</link>
      <guid>https://dev.to/magnificode/building-opentrainer-real-time-workout-tracking-with-convex-and-nextjs-59h4</guid>
      <description>&lt;p&gt;I've been lifting for years. And I've tried every workout app out there. Strong. Hevy. FitNotes. Ladder. They all fall short in the same predictable way.&lt;/p&gt;

&lt;p&gt;You're 30 seconds into your rest period, sweaty, trying to log a set, and the app wants you to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tap "Add Set"&lt;/li&gt;
&lt;li&gt;Tap the weight field&lt;/li&gt;
&lt;li&gt;Wait for the keyboard&lt;/li&gt;
&lt;li&gt;Type the weight&lt;/li&gt;
&lt;li&gt;Dismiss the keyboard&lt;/li&gt;
&lt;li&gt;Tap the reps field&lt;/li&gt;
&lt;li&gt;Wait for the keyboard AGAIN&lt;/li&gt;
&lt;li&gt;Type the reps&lt;/li&gt;
&lt;li&gt;Dismiss the keyboard AGAIN&lt;/li&gt;
&lt;li&gt;Scroll to find the "Save" button&lt;/li&gt;
&lt;li&gt;Tap "Save"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By this point your rest timer expired 45 seconds ago and you're late for your next set.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Was Solving
&lt;/h2&gt;

&lt;p&gt;Most workout apps treat logging like you're sitting at a desk with a cup of coffee and unlimited time.&lt;/p&gt;

&lt;p&gt;Meanwhile, in the real world, you've got maybe 60-90 seconds between sets.&lt;/p&gt;

&lt;p&gt;So we built &lt;a href="https://opentrainer.app" rel="noopener noreferrer"&gt;OpenTrainer&lt;/a&gt;. Two taps per set.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack (And Why We Chose It)
&lt;/h2&gt;

&lt;p&gt;Here's what we're running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16&lt;/li&gt;
&lt;li&gt;Convex&lt;/li&gt;
&lt;li&gt;Clerk&lt;/li&gt;
&lt;li&gt;OpenRouter&lt;/li&gt;
&lt;li&gt;shadcn&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Convex?
&lt;/h3&gt;

&lt;p&gt;Convex is awesome for this use case.&lt;/p&gt;

&lt;p&gt;Traditional workout apps use REST APIs. You tap "Log Set" → HTTP request → server processes → database write → HTTP response → UI updates. If you're on sketchy gym WiFi, that's 2-3 seconds of loading spinner hell.&lt;/p&gt;

&lt;p&gt;Convex gives us real-time sync with zero setup. The mutation fires, the optimistic update shows instantly, and the actual database write happens in the background. If it fails, it rolls back automatically. If you go offline mid-workout, it queues the mutations and syncs when you're back online.&lt;/p&gt;

&lt;p&gt;We have optimistic updates working with &lt;strong&gt;seven lines of code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useConvexMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workouts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addLiftingEntry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleLogSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;logSet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;workoutId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;exerciseName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bench Press&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateClientId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Deduplication&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lifting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lifting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;setNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;225&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// UI updates instantly. Done.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Convex handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimistic UI updates&lt;/li&gt;
&lt;li&gt;Deduplication via &lt;code&gt;clientId&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Offline queueing&lt;/li&gt;
&lt;li&gt;Conflict resolution&lt;/li&gt;
&lt;li&gt;Real-time sync across devices&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Schema
&lt;/h3&gt;

&lt;p&gt;We use a discriminated union pattern for entries. One table, multiple exercise types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// entries table&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lifting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cardio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mobility&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lifting&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;setNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;rpe&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;cardio&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;durationSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;intensity&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;mobility&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;holdSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;perSide&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This beats having separate tables for &lt;code&gt;lifting_entries&lt;/code&gt;, &lt;code&gt;cardio_entries&lt;/code&gt;, &lt;code&gt;mobility_entries&lt;/code&gt;. Querying a workout's full history is a single query. This way if we want to add a new exercise category we just extend the union.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js 16
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What worked:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel routes made the bottom navigation clean&lt;/li&gt;
&lt;li&gt;Metadata API makes SEO easier&lt;/li&gt;
&lt;li&gt;Clerk's new &lt;code&gt;clerkMiddleware&lt;/code&gt; (v5+) works perfectly with Next.js 16&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What hurt:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The dev server randomly decides to recompile everything. Often.&lt;/li&gt;
&lt;li&gt;Hot reload occasionally needs a manual refresh&lt;/li&gt;
&lt;li&gt;Some Convex types needed manual casting (minor annoyance)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Auth Setup:&lt;/strong&gt; Clerk + Convex requires a JWT template in the Clerk dashboard. Standard stuff, nothing custom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// convex/auth.config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLERK_JWT_ISSUER_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;applicationID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;convex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You just need to grab your JWT issuer URL from Clerk and set it as an env var. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Layer
&lt;/h2&gt;

&lt;p&gt;Here's where we diverged from every other fitness app.&lt;/p&gt;

&lt;p&gt;Most apps with "AI" just slap ChatGPT on a prompt and call it a day. We wanted something smarter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why OpenRouter Instead of Direct APIs
&lt;/h3&gt;

&lt;p&gt;We use &lt;strong&gt;OpenRouter&lt;/strong&gt; as our AI gateway. One API, multiple models. Right now we're using &lt;strong&gt;Gemini 3 Flash&lt;/strong&gt; through OpenRouter's unified endpoint.&lt;/p&gt;

&lt;p&gt;Why OpenRouter?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No vendor lock-in&lt;/strong&gt; - Switch models with one line of code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost optimization&lt;/strong&gt; - OpenRouter shows pricing for every model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback logic&lt;/strong&gt; - Can implement automatic failover to Claude/GPT if Gemini is down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One API key&lt;/strong&gt; - Don't need separate Google, Anthropic, OpenAI accounts
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://openrouter.ai/api/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENROUTER_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;google/gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userMessage&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Routine Generation Problem
&lt;/h3&gt;

&lt;p&gt;Ask GPT-4 to generate a workout routine and you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generic "bro split" garbage&lt;/li&gt;
&lt;li&gt;Exercises you don't have equipment for&lt;/li&gt;
&lt;li&gt;Zero consideration for your actual goals&lt;/li&gt;
&lt;li&gt;Hallucinated exercise names ("reverse cable overhead skull-tricep extension")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built an &lt;strong&gt;equipment audit&lt;/strong&gt; during onboarding. User checks off what they have access to. Planet Fitness member? Cool, we know you have Smith machines and dumbbells, not barbells. Home gym? Tell us what you've got.&lt;/p&gt;

&lt;p&gt;Then we pass that to Gemini with a structured prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routinePrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
Generate a &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; program for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;experienceLevel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; lifter.

EQUIPMENT AVAILABLE:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;equipment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;

CONSTRAINTS:
- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;weeklyAvailability&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; days per week
- &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sessionDuration&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; minutes per session
- Must use ONLY the equipment listed above
- Use standard exercise names (no made-up movements)

OUTPUT FORMAT: JSON matching this schema...
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works &lt;strong&gt;way better&lt;/strong&gt; than you'd expect. Gemini's actually pretty good at constraint satisfaction when you give it clear boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Training Load Calculation
&lt;/h3&gt;

&lt;p&gt;Most apps calculate "volume" as &lt;code&gt;sets × reps × weight&lt;/code&gt;. That's... fine. But it doesn't account for intensity.&lt;/p&gt;

&lt;p&gt;We use a modified training load formula that factors in RPE (Rate of Perceived Exertion):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateTrainingLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lifting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rpe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lifting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reps&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intensityFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rpe&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;volume&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;intensityFactor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A set of 5 reps at 225 lbs with RPE 9 (brutal) counts for more than 10 reps at 135 lbs with RPE 5 (warm-up). This gives us way better insights into actual training stress.&lt;/p&gt;

&lt;h2&gt;
  
  
  The UI Philosophy
&lt;/h2&gt;

&lt;p&gt;Here's the entire interaction model for logging a set:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tap the weight number&lt;/strong&gt; → Quick adjust (+5 lb) or type any value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tap "Log Set"&lt;/strong&gt; → Done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We use large tap targets (minimum 48px). Everything important is in thumb-reach on a phone held one-handed. The rest timer starts automatically after you log a set. Haptic feedback on every interaction so you get confirmation even with sweaty hands.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stepper Component
&lt;/h3&gt;

&lt;p&gt;Building good steppers for weight/rep adjustment was surprisingly tricky:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SetStepper&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setWeight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// +/- 5 lbs per tap&lt;/span&gt;
  &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Weight"&lt;/span&gt;
  &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lb"&lt;/span&gt;
  &lt;span class="na"&gt;enableDirectInput&lt;/span&gt;  &lt;span class="c1"&gt;// Tap the number to type&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're using optimistic updates here too. The value updates in local state immediately, then fires a debounced mutation to Convex to persist it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stuff That Broke
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem #1: The Rest Timer Kept Getting Killed
&lt;/h3&gt;

&lt;p&gt;Mobile browsers are aggressive about killing background JavaScript. We had users complaining the rest timer would stop if they switched to Spotify between sets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Web Workers + Service Workers + loud-ass notifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rest-timer-worker.ts&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;timeRemaining&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getRemainingTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus we request notification permissions so we can send an annoying alert when rest time is up. It works. Users hate it. But they also don't miss their rest periods anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #2: Clerk JWT Validation Failing Randomly
&lt;/h3&gt;

&lt;p&gt;Convex needs to validate Clerk JWTs. The issuer domain kept changing between &lt;code&gt;clerk.accounts.dev&lt;/code&gt; and &lt;code&gt;accounts.dev&lt;/code&gt; for a reason I'm still not clear on.&lt;/p&gt;

&lt;p&gt;We set up a &lt;code&gt;.env&lt;/code&gt; var for the issuer domain and made it configurable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #3: The "My Phone Died Mid-Workout" Problem
&lt;/h3&gt;

&lt;p&gt;Users would start a workout, log 10 sets, phone dies, come back and... everything's gone.&lt;/p&gt;

&lt;p&gt;We added a recovery mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On app load, check for incomplete workout&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkForIncompleteWorkout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeWorkout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getActiveWorkout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeWorkout&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;activeWorkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in_progress&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Show "Resume workout?" dialog&lt;/span&gt;
    &lt;span class="nf"&gt;showResumeDialog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;activeWorkout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if your phone dies, the workout stays in "in_progress" state. When you open the app again, it asks if you want to resume. Crisis averted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We'd Do Differently
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. User Testing Sooner
&lt;/h3&gt;

&lt;p&gt;We built the "perfect" UI in our heads, then watched actual users struggle with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;OpenTrainer is in alpha. It's free. No ads. No tracking pixels. Your data exports as JSON whenever you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://opentrainer.app" rel="noopener noreferrer"&gt;Try it here&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or clone the repo and run it yourself. It's Apache 2.0 licensed. Do whatever you want with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/house-of-giants/opentrainer" rel="noopener noreferrer"&gt;GitHub: house-of-giants/opentrainer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;We built a workout tracker that doesn't suck. Two taps per set. Real-time sync. AI that actually knows your gym has a Smith machine, not a barbell. Next.js + Convex + Clerk + OpenRouter.&lt;/p&gt;

&lt;p&gt;The hardest parts weren't the tech. They were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Making everything feel &lt;strong&gt;instant&lt;/strong&gt; (optimistic updates everywhere)&lt;/li&gt;
&lt;li&gt;Keeping timers alive on mobile (Web Workers + notifications)&lt;/li&gt;
&lt;li&gt;Building UI that works with sweaty hands (big tap targets, haptic feedback)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building something similar, use Convex. Seriously. It's rad for real-time apps.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>learning</category>
      <category>showdev</category>
    </item>
    <item>
      <title>A searchable reference for WordPress Gutenberg block markup</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 15 Jul 2025 16:38:03 +0000</pubDate>
      <link>https://dev.to/magnificode/a-searchable-reference-for-wordpress-gutenberg-block-markup-36il</link>
      <guid>https://dev.to/magnificode/a-searchable-reference-for-wordpress-gutenberg-block-markup-36il</guid>
      <description>&lt;p&gt;I built out a documentation site for the insane block markup associated with WordPress's Gutenberg blocks. There was sparse documentation around this, and parsing the &lt;a href="https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src" rel="noopener noreferrer"&gt;WP Block GitHub&lt;/a&gt; was ridiculous, especially as someone trying to push production work out into the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Exists
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Block comment syntax is often poorly documented&lt;/li&gt;
&lt;li&gt;HTML output varies between themes and contexts&lt;/li&gt;
&lt;li&gt;FSE markup quirks are scattered across various resources&lt;/li&gt;
&lt;li&gt;Real-world examples are hard to find in one place&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the site provides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Complete HTML markup reference for WordPress core blocks&lt;/li&gt;
&lt;li&gt;✅ Interactive examples with syntax highlighting&lt;/li&gt;
&lt;li&gt;✅ Copy-paste ready code snippets&lt;/li&gt;
&lt;li&gt;✅ Block validation tools&lt;/li&gt;
&lt;li&gt;✅ FSE quirks and gotchas documentation&lt;/li&gt;
&lt;li&gt;✅ Best practices and guidelines&lt;/li&gt;
&lt;li&gt;✅ Comprehensive resource directory&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Comprehensive Block Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Core Blocks: Complete documentation for most common WordPress core blocks&lt;/li&gt;
&lt;li&gt;Markup Examples: Real-world HTML output with explanations&lt;/li&gt;
&lt;li&gt;Properties Reference: Detailed attribute and property documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Developer Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Block Validator: Validate your block markup for errors&lt;/li&gt;
&lt;li&gt;Pattern Library: Ready-made block combinations&lt;/li&gt;
&lt;li&gt;Code Highlighting: Syntax-highlighted examples throughout&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learning Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Best Practices: WordPress block development guidelines&lt;/li&gt;
&lt;li&gt;FSE Quirks: Known issues and workarounds&lt;/li&gt;
&lt;li&gt;Resource Directory: Curated links to essential tools and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions and feedback welcome 🤘&lt;/p&gt;

&lt;p&gt;Documentation Site: &lt;a href="https://www.wpblockdocs.com/" rel="noopener noreferrer"&gt;https://www.wpblockdocs.com/&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/house-of-giants/wp-block-docs" rel="noopener noreferrer"&gt;https://github.com/house-of-giants/wp-block-docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>programming</category>
      <category>webdev</category>
      <category>documentation</category>
    </item>
    <item>
      <title>🚀 Building a Role Based Navigation in NextJS using Clerk</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Wed, 30 Oct 2024 15:44:27 +0000</pubDate>
      <link>https://dev.to/magnificode/building-a-role-based-navigation-in-nextjs-using-clerk-2bk4</link>
      <guid>https://dev.to/magnificode/building-a-role-based-navigation-in-nextjs-using-clerk-2bk4</guid>
      <description>&lt;h2&gt;
  
  
  💥 The Challenge
&lt;/h2&gt;

&lt;p&gt;We’ve been working on a few projects with fairly complex navigation systems here at &lt;a href="https://houseofgiants.com" rel="noopener noreferrer"&gt;House of Giants&lt;/a&gt;. Different user types need to see different parts of an application and are separate in what they can see based on the role that they’re assigned. As these applications grow these navigation systems can become unwieldy. We’ve developed a system that we think works pretty well.&lt;/p&gt;

&lt;p&gt;At a high level, here are a few of the things we’ve found that make role-based navigation tricky:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚖️ Duplicated items across various roles&lt;/li&gt;
&lt;li&gt;😵‍💫 Hard-to-maintain code when you need to update shared elements&lt;/li&gt;
&lt;li&gt;🔐 Complex logic around who can see what&lt;/li&gt;
&lt;li&gt;🔄 Difficulty in adding new roles or items without breaking things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see how we can alleviate some of these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Step 1: A Modular Navigation Structure
&lt;/h2&gt;

&lt;p&gt;Using a modular approach gives us the ability to change only what we need, only for the user types that are affected. Keeping logic separate ensures that changing one piece doesn’t create a litany of issues for other users.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Define Individual Navigation Items
&lt;/h3&gt;

&lt;p&gt;Start by setting up a &lt;strong&gt;single source of truth&lt;/strong&gt; for all navigation items. This keeps things consistent and eliminates redundancies. These are just basic objects, customize them and their data based on your needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// items.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Admin Dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;cool&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard/admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;myTasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;,&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard/profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;    &lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create Reusable Navigation Sets
&lt;/h3&gt;

&lt;p&gt;At this point we found it helpful to define some commonly used groupings. This lets us build different navigation layouts without repeating ourselves. ✨&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sets.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navSets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;userEssentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myTasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;taskManagement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myTasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;adminEssentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Organize Navigation into Sections
&lt;/h3&gt;

&lt;p&gt;With items and sets defined, we can further organize the navigation into logical sections. This keeps everything structured and makes it easy to add or remove sections later. We’ve opted to create a new &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set" rel="noopener noreferrer"&gt;Set&lt;/a&gt; to further ensure that no duplicate routes are provided. A value in a set may only occur once, duplicates are omitted.&lt;/p&gt;

&lt;p&gt;In our case, we’re adding a &lt;code&gt;text&lt;/code&gt; value to our sections, this acts as the parent dropdown label for the set of navigation items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sections.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;navSets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./sets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;navSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;adminEssentials&lt;/span&gt;&lt;span class="p"&gt;])],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;navSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userEssentials&lt;/span&gt;&lt;span class="p"&gt;])],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Configure Role-Based Access
&lt;/h3&gt;

&lt;p&gt;Here’s where we map user roles to the sections they should see. This keeps role-based logic centralized and easy to adjust 🔒 – keep in mind here, we’re using &lt;a href="https://clerk.com/" rel="noopener noreferrer"&gt;Clerk&lt;/a&gt; role names here, yours may be different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// roles.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roleConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;org:admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;org:user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Build the Navigation Hook
&lt;/h3&gt;

&lt;p&gt;We decided that building a custom hook here would serve us best when building the UI for the navigation. Our hook assembles the navigation based on the user’s role and will return only the sections relevant to each user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./hooks/navigation/useNavigation.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;navItems&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/config/navigation/items&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sections&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/config/navigation/sections&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;roleConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/config/navigation/roles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getNavigationItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userRole&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sectionNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;roleConfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userRole&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;roleConfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;org:user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sectionNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sectionName&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sectionName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemKey&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;navItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;itemKey&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useNavigation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sessionClaims&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionClaims&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;org_role&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;org:user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navigationItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getNavigationItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userRole&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;navigationItems&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll see above that we’re creating a function called &lt;code&gt;getNavigationItems&lt;/code&gt; and passing this function the userRole from Clerk. We then define the &lt;code&gt;sectionNames&lt;/code&gt; based on that userRole, or default to the nav a basic user would see, providing us with a clean fallback.&lt;/p&gt;

&lt;p&gt;We then map over those section names and return the title as well as the nested navigation items.&lt;/p&gt;

&lt;p&gt;Finally, we define our hook. We get the User’s role from Clerk using the sessionClaims object that is returned with &lt;code&gt;await auth()&lt;/code&gt;, thus returning our navigation object, which will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;,&lt;/span&gt;&lt;span class="err"&gt; 
&lt;/span&gt;        &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;        &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard/profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;        &lt;span class="na"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🌟 Why This Works
&lt;/h3&gt;

&lt;p&gt;This setup has some awesome advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single Source of Truth&lt;/strong&gt; 📚: All navigation items are defined once, cutting down on duplication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composable Structure&lt;/strong&gt; 🧩: You can create new sections easily by reusing existing sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy Maintenance&lt;/strong&gt; 🔧: Shared items only need updates in one place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt; 📈: Adding new roles or navigation items is a breeze.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt; ✅: Using keys helps catch typos and enables IDE autocompletion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logical Structure&lt;/strong&gt; 📂: Sections are organized in a clear, modular way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example Usage
&lt;/h2&gt;

&lt;p&gt;Here’s how you might use this navigation structure in a &lt;code&gt;Sidebar&lt;/code&gt; component:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File: &lt;code&gt;Sidebar.js&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sidebar.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useNavigation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/hooks/useNavigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Sidebar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;navigationItems&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useNavigation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;navigationItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;section&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;))}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/nav&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📝 Best Practices
&lt;/h2&gt;

&lt;p&gt;A few tips to keep your navigation organized and manageable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📁 Store navigation items in a separate configuration folder.&lt;/li&gt;
&lt;li&gt;✨ Use descriptive names for navigation sets and sections.&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;Set&lt;/code&gt; to avoid duplicates.&lt;/li&gt;
&lt;li&gt;🔒 Set defaults for unknown user &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach provides a solid foundation for a scalable, maintainable navigation structure. By organizing navigation into modular components, you get a flexible system that grows with your app—without becoming an absolute nightmare to maintain.&lt;/p&gt;

&lt;p&gt;Remember, the goal is to strike a balance between flexibility and simplicity. This setup does just that, making it practical for real-world applications. 👌&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Thank You ❤️</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 18 Jun 2024 21:29:49 +0000</pubDate>
      <link>https://dev.to/magnificode/a-thank-you-3od1</link>
      <guid>https://dev.to/magnificode/a-thank-you-3od1</guid>
      <description>&lt;p&gt;✨ An army of 10,000 of you have hit the follow button next to my name.✨&lt;/p&gt;

&lt;p&gt;That's far and away the largest number of people that have knowingly hit a button to engage with me in the 25+ years that I've existed on the internet.&lt;/p&gt;

&lt;p&gt;That's a ludicrously large number to me, and it felt like a milestone worth celebrating. 🤘&lt;/p&gt;

&lt;p&gt;With that, I just wanted to throw out a whole hearted thank you to everyone here. I've been writing on Dev.to off and on for just over 5 years now and it's been nothing but welcoming, and filled with enjoyable interactions with folks in the community.&lt;/p&gt;

&lt;p&gt;Community is hard to find, but when you do find it, nurture it and enjoy it.&lt;/p&gt;

&lt;p&gt;Thank you all very much!&lt;/p&gt;

</description>
      <category>community</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Some UX Design Principles Everyone Should Know 🥸</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 18 Jun 2024 18:13:14 +0000</pubDate>
      <link>https://dev.to/magnificode/some-ux-design-principles-everyone-should-know-293k</link>
      <guid>https://dev.to/magnificode/some-ux-design-principles-everyone-should-know-293k</guid>
      <description>&lt;p&gt;Hitting the ground running with a new app idea is tough. There are a million things to do and no time to do them. You’ve gone through the “justification” phase, explaining to everyone and their mom why the world needs your application. You’ve documented every single forecast and business plan. Now it’s time to start thinking about actually executing your vision. It all begins with User Experience. Great UX design isn't an afterthought or a “nice to have”; it's an absolute necessity for any application aiming to stand out and thrive. That said, let's dive into some UX design principles that everyone should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  User-Centered Design 🗣️
&lt;/h2&gt;

&lt;p&gt;First things first: always put your users at the center of your design process. This means understanding their needs, behaviors, and pain points. It's not about what you think looks good; it's about making sure your users can accomplish the goal you’ve set out for them with the least amount of resistance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conduct User Research&lt;/strong&gt;: Surveys, interviews, and usability tests are your best friends here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Personas&lt;/strong&gt;: Develop detailed profiles of your target users to guide your design decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Journey Mapping&lt;/strong&gt;: Map out the steps users take to complete tasks within your application, to identify opportunities for improvement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Simplicity and Clarity 🧘
&lt;/h2&gt;

&lt;p&gt;Less is more. Seriously. Don't overload your users with information or options. Keep your design clean, straightforward, and intuitive. As users flock to your application, you’ll be able to take their feedback and make well educated, data-driven decisions about what to improve.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear Navigation&lt;/strong&gt;: Make sure users can easily find what they need without getting lost. Don’t hide primary actions behind layers of interaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimalist Design&lt;/strong&gt;: Remove any unnecessary elements that don't add value. Truly ask yourself why something exists, and how it aids in users accomplishing their goals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Readable Content&lt;/strong&gt;: Use clear, concise language and break up text with headings, bullet points, or some &lt;em&gt;very&lt;/em&gt; cool emojis if you’re hip 😎.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Consistency
&lt;/h2&gt;

&lt;p&gt;Like all things, consistency is key. Especially when it comes to creating a seamless user experience. This means maintaining uniformity in your design elements across your website or app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Design System&lt;/strong&gt;: I love design systems. There’s nothing better than an organized style guide that includes typography, color schemes, button styles, and all the little components that make your application unique.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Interactions&lt;/strong&gt;: Make sure similar actions produce similar results throughout your site. Don’t have twelve different variations of that modal. People notice, and it makes you look silly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branding&lt;/strong&gt;: Keep your branding elements consistent to build trust and foster that brand recognition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Accessibility
&lt;/h2&gt;

&lt;p&gt;Accessibility needs to be top of mind at the outset of any project. Baking accessibility best practices into the UX and design phase of your application will ensure you’re providing an equitable experience for each and every one of your users, regardless of how they interact with the web. Make sure you’re &lt;em&gt;at a minimum&lt;/em&gt; making these considerations: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alt Text for Images&lt;/strong&gt;: This is too easy not to be doing. Provide descriptive text for images to assist screen readers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard Navigation&lt;/strong&gt;: Not everyone browses the web with a mouse or a $130 Apple trackpad. Make considerations for those who navigate with a keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contrast Ratios&lt;/strong&gt;: I’m old and this one bothers me more and more. Do a quick check to make your text has &lt;em&gt;at least&lt;/em&gt; a AA contrast ratio. There &lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;are&lt;/a&gt; &lt;a href="https://coolors.co/contrast-checker/112a46-acc8e5" rel="noopener noreferrer"&gt;several&lt;/a&gt; &lt;a href="https://colourcontrast.cc/" rel="noopener noreferrer"&gt;tools&lt;/a&gt; &lt;a href="https://accessibleweb.com/color-contrast-checker/" rel="noopener noreferrer"&gt;for&lt;/a&gt; &lt;a href="https://contrastchecker.com/" rel="noopener noreferrer"&gt;this&lt;/a&gt;. Pick one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback and Responsiveness
&lt;/h2&gt;

&lt;p&gt;In our wild world of JavaScript applications, ensuring that a user knows that &lt;em&gt;something&lt;/em&gt; is happening, has become more important than in the past. Our applications connect to more third-party services these days, and our users must know that an action they’ve taken is processing. We use these strategies to help with that;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loading Indicators / Skeletons 💀&lt;/strong&gt;: Skeleton loaders not only sound cool, they also provide anticipatory design elements that give users a sense of what will be on the page, before the data has fully loaded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Messages:&lt;/strong&gt; Provide clear, helpful error messages when something goes wrong. There’s nothing worse than a user seeing &lt;code&gt;Uncaught ReferenceError: Invalid left-hand side in assignment&lt;/code&gt; or some other nonsense. Give them plain English feedback instead!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Optimization:&lt;/strong&gt; This is becoming more and more tricky. The more services we integrate, the more complex a database becomes the more creative you have to get to ensure an application is performant. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Emotional Design
&lt;/h2&gt;

&lt;p&gt;This is such a cool concept to us. Emotional design focuses more on creating experiences that evoke a particular feeling from your users. As cliche, as it may sound, this creates a unique connection and keeps your users feeling positive about the service you’re providing them. This can be done in a few ways;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storytelling&lt;/strong&gt;: Visual storytelling on the web is an art form. We love expressing our creativity through creating a full-fledged digital experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Micro-Interactions&lt;/strong&gt;: Small, thoughtful animations and interactions tend to put you in a position to entertain and engage your users and drive them to keep coming back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human Touch&lt;/strong&gt;: Incorporate human elements, like friendly language, relatable imagery, or exceptionally witty content, like the content you’re reading right this second.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Iterative Design
&lt;/h2&gt;

&lt;p&gt;Everything is iterative. After you build an MVP, it’s back to the drawing board. Starting the process over and continuously iterating is the only way to keep pace with the multi-billion dollar applications that exist in our world. Stay engaged with your user base, listen to their feedback, and they’ll remain loyal to you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Usability Testing&lt;/strong&gt;: Regularly test your designs with real users. This is and always will be something that the tech giants miss out on. Don’t miss the mark here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B Testing&lt;/strong&gt;: Get super granular with this. A/B test simple language, or small components, and use that information to inform the larger design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics&lt;/strong&gt;: Use data to inform all of your decisions. There needs to be a why, and data will uncover it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Focus on Business Goals
&lt;/h2&gt;

&lt;p&gt;While user satisfaction is paramount, your application should align with your business objectives. All that work you did at the inception of your idea shouldn’t go to waste. The forecasts and the documentation are invaluable, but balance user needs with your goals. No bullet points here, those business goals are yours to define. We’ll be here to help you refine and execute them.&lt;/p&gt;

&lt;p&gt;Keep testing, collaborating, learning, and evolving.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>design</category>
    </item>
    <item>
      <title>Raising a Web Studio Without Selling Your Soul 👹</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 04 Jun 2024 15:47:31 +0000</pubDate>
      <link>https://dev.to/magnificode/raising-a-web-studio-without-selling-your-soul-41ib</link>
      <guid>https://dev.to/magnificode/raising-a-web-studio-without-selling-your-soul-41ib</guid>
      <description>&lt;p&gt;Building a business is hard. That’s a pretty obvious statement, but there’s so much nuance to it that it bears repeating. How am I going to get more work? How am I going to finish the work that I have? What about taxes? Did I send out that status update on time? Are my clients properly informed about the status of their projects? Should I start scaling? Shit. It’s a lot to think about. I’ve committed to building House of Giants in a way that ensures it will never become a soulless code farm churning out sub-par websites for profit. Here’s what it’s taken to get us this far. &lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding the Scale-At-All-Costs Mind Game ⚖️
&lt;/h2&gt;

&lt;p&gt;I've seen it too many times and to too many awesome companies. The critical point where businesses decide to scale aggressively to take on more work, build faster, churn churn churn. This often means rapid hiring, taking on projects that aren’t the right fit, and pushing quick, generic solutions that don’t scale. Sure, it might bring short-term gains, but it usually leads to long-term headaches. A marked reduction in quality and a noticeable uptick in employee turnover. Nobody is happy doing that work. And the voices of the passionate few get drowned out by the shrieks of account managers making promises nobody can keep.&lt;/p&gt;

&lt;p&gt;House of Giants was built to avoid these distractions. We focus on sustainable and thoughtful growth, putting everything we have into the work we love.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personalized Solutions Over One-Size-Fits-All 👖
&lt;/h2&gt;

&lt;p&gt;Another massive issue with traditional agencies is their tendency to use the same set of cookie-cutter tech for everyone, with little to no thought into how that tech should grow with a business. This approach might make some processes smoother, but it often fails to address the unique challenges each client faces.&lt;/p&gt;

&lt;p&gt;When we start a new project, we dive deep into our partner’s business, objectives, hopes, dreams, and desires. We conduct thorough discovery sessions to understand the specific challenges and goals of each project we take on. This means &lt;strong&gt;asking the right questions&lt;/strong&gt; to get to the root of our partner’s issues and finding the best way to address them.&lt;/p&gt;

&lt;p&gt;Every project is different. Sometimes the solution is a robust content management system; other times, it's a custom web application or a mix of several integrations. We tailor our technology recommendations to fit each of our partner’s needs, ensuring the solution we provide them can grow and evolve with their business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emphasizing Collaboration and Partnership 🤝
&lt;/h2&gt;

&lt;p&gt;I can’t tell you how many times I’ve seen people passed from account manager to project manager, to executive and then back down to the production team before any meaningful collaboration has even begun. It’s exhausting, and business owners shouldn’t have to go through that when embarking on a website build. This only serves to break down communication and ensure that some key feature or functionality is built poorly, or not at all.&lt;/p&gt;

&lt;p&gt;House of Giants prides itself on maintaining a small, supremely passionate team. Maintaining the small but mighty approach allows us to work closely with our partners, fostering genuine collaboration and partnership. This enables us to truly integrate with our partners and become an extension of them. We find immense satisfaction in understanding the issues of our partners and finding creative ways to solve them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Flexible Foundation 🧘
&lt;/h2&gt;

&lt;p&gt;We build solutions that solve our partners’ immediate problems and adapt as their business evolves. This means choosing technologies that are flexible and scalable, allowing for growth and change without needing a complete overhaul.&lt;/p&gt;

&lt;p&gt;For small business owners, having a flexible technology foundation is crucial. Markets change, customer bases shift, and businesses need to adapt quickly. Our approach ensures that our partners are never locked into a single solution but have the tools and flexibility to pivot as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staying Connected to the Work 🔌
&lt;/h2&gt;

&lt;p&gt;One of my biggest fears in scaling House of Giants is losing touch with the production work. So many business strategists and YouTube evangelists advocate for scaling to free up the owner’s time, but this often results in a massive disconnect from the fun of the production. If we’re not enjoying at least some of what we do daily then why are we doing it at all? Every single time, when leadership becomes disconnected from production, it results in a painful tone-deaf existence and yields massive distrust from anyone else within the production team.&lt;/p&gt;

&lt;p&gt;To combat this, I stay involved and thrive in the production process. This helps me maintain a clear vision of how House of Giants can improve and innovate. It also ensures that I remain connected to the quality of our work and the happiness of the partners and colleageues we choose to work with.&lt;/p&gt;

&lt;p&gt;All this is to say that building a successful web studio doesn’t mean losing touch with your values or the quality of the work you produce. By avoiding the scale-at-all-costs mentality, focusing on thoughtfully built solutions, fostering collaboration, and staying intimately familiar with the production process, you can create a studio that not only thrives but also delivers exceptional value to your partners.&lt;/p&gt;

&lt;p&gt;Running a web studio, or any business for that matter, is hard as hell. But with the right approach, it’s possible to grow sustainably while maintaining the integrity and quality that sets you apart. Let’s build something great together, without losing sight of what actually matters. ❤️&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>learning</category>
      <category>career</category>
      <category>community</category>
    </item>
    <item>
      <title>The Authenticity Wars - SEO Strategies In The Age Of AI 🤖</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Mon, 15 Apr 2024 14:43:45 +0000</pubDate>
      <link>https://dev.to/magnificode/the-authenticity-wars-seo-strategies-in-the-age-of-ai-n2l</link>
      <guid>https://dev.to/magnificode/the-authenticity-wars-seo-strategies-in-the-age-of-ai-n2l</guid>
      <description>&lt;p&gt;SEO was always one of those things that made me cringe every time I heard the phrase. I always envisioned cramming keywords into content just for the sake of their existence on the page. Endlessly looping phrases like “Denver web development” or “WordPress Development Denver web dev” are unnatural and meaningless. This practice inevitably left site visitors with information that didn’t convey anything useful.&lt;/p&gt;

&lt;p&gt;Luckily these days authentic content that genuinely serves your audience’s needs inherently ranks higher and performs better than the keyword-stuffing days of the past.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dark Ages 😨
&lt;/h2&gt;

&lt;p&gt;Since the dark days of keyword stuffing and aggressive backlinking, SEO and the algorithms that drive the machine have evolved to prioritize content relevance over keyword frequency. This is such a welcome change from how aggressively agencies were working to game the system, trying to outsmart Google to rank #1 for “Denver web development design Denver web agency”. A notable shift occurred with the introduction of Google’s RankBrain. This AI algorithm works to understand search queries and deliver relevant results by analyzing how users interact with the search results themselves, and learn from that interaction. Before RankBran, this was done manually by developers and prompted (virtual) riots anytime the algorithm changed.&lt;/p&gt;

&lt;p&gt;After learning about this change, and understanding that RankBrain increases or decreases the importance of various SEO metrics based on the keyword, I became slightly more satisfied with the state of SEO. It’s a step in the right direction to serve results based on relevancy rather than keyword density. It also forces us to generate content that is genuinely useful and engaging, which results in a better experience for the end user.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Authenticity? 🤷
&lt;/h2&gt;

&lt;p&gt;I did a little research for this post trying to find examples of truly authentic content, which by the way, is becoming increasingly more difficult in the age of ChatGPT (I’m guilty of using AI to write content from time to time because god damn is it hard to organize the thoughts in my brain into words on a page). But I came across Tim Urban’s site &lt;a href="https://waitbutwhy.com/" rel="noopener noreferrer"&gt;Wait but Why&lt;/a&gt; and started reading. Apparently like 20 minutes have passed since I stumbled across the site. I wanted to stay there, and keep reading because the content was genuine human experience and commentary. This is something that’s lost in our industry all too easily. We worked hard at House of Giants to tailor our voice, and we get complimented on it almost every time we speak to a prospective partner. Using your genuine voice in your content, no matter how boring the subject matter may be, is going to be increasingly important to encourage people to stay on your site and engage with you and your business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Black Hat SEO 🎩
&lt;/h2&gt;

&lt;p&gt;I would have much rather used a fedora emoji for this section if one existed. In any case, if you’re not convinced by my heartfelt call to action about authenticity, then maybe the scared straight approach is a better one for you. Since the SEO grifter became popular ten-ish years ago, search engines have become wise to the games that were being played. These days keyword stuffing, excessive backlinking, cloaking, link farms, and content automation are all events that search engines like Google are getting better at detecting. Some SEO companies will employ varying levels of these strategies to give a quick, seemingly authentic boost in rankings for their clients. Proving their success they take their payment and run. Over time, as these strategies get picked up by Google, a site that employs these doomed strategies may face drastically reduced visibility and overall credibility. There’s no doubt that if you care about your business, you care about the perception of your business too. Users can tell when content isn’t genuine, and that has an effect on your reputation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Find Authenticity Together 💬
&lt;/h2&gt;

&lt;p&gt;I challenge you to find genuinely authentic content sources. What makes them successful, what makes you want to keep reading? Share them with us. The more automated the world gets the more we digress from the awe of the infinite community that the internet promised us. Let’s encourage writing that resonates with us all so we can learn how to be more authentic ourselves.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🧐 Rethinking WordPress: The Pros and Cons of Going Headless</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Tue, 02 Apr 2024 17:02:57 +0000</pubDate>
      <link>https://dev.to/magnificode/rethinking-wordpress-the-pros-and-cons-of-going-headless-134b</link>
      <guid>https://dev.to/magnificode/rethinking-wordpress-the-pros-and-cons-of-going-headless-134b</guid>
      <description>&lt;p&gt;Ah yes, the first quarter of the year is done. Marketing budgets have been set, and for some, that means embarking on the journey of a website rebuild. You’re familiar with WordPress, but your experience with it in the past has been harrowing. Bloated, slow, a pain in the ass to update. You might have developers on one hand shouting from the rooftops to use anything other than WordPress. On the other you have some pushing pre-built templates in your direction as if they’ve crafted them from marble themselves. It’s a lot to process.&lt;/p&gt;

&lt;p&gt;I want to take the opportunity to write a few posts that provide some insight into considerations I make when choosing a technology stack for projects at House of Giants. Today, we’ll explore the differences between a traditional WordPress build and Headless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Traditional: Ol’ Reliable WordPress
&lt;/h2&gt;

&lt;p&gt;Traditional WordPress builds are rich in features and supported by a robust community, making it a solid choice for many projects. Let’s take a look at some of the positives that make traditional WordPress builds a compelling option for many:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚔️ Good Themes Go Far:&lt;/strong&gt; Well-crafted themes are hard to come by, and finding one of these gems isn't always straightforward. But when you do, the benefits can be well worth it. Great SEO, performant, and accessible, a thoughtfully built WordPress theme will last you years, and enable you to seamlessly manage your content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🦑 A Community of Giants:&lt;/strong&gt; One of WordPress's most invaluable assets is its vast, well-established community. This network of users and developers creates an environment where troubleshooting becomes less intimidating and far more manageable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📡 Timeless Technology:&lt;/strong&gt; Traditional WordPress is built using a server-side rendered approach, where PHP is responsible for rendering the site. This means all of your content is easily available to be crawled by Google. The result is classic, easy-to-read HTML, CSS, and JavaScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🛋 A Comfortable Dashboard:&lt;/strong&gt; WordPress has always provided an excellent editing experience for folks who need it. Pair this with the illusive well-built theme, and managing your content can become as stress-free as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the flip side, a traditional WordPress build isn't without its flaws. Certain aspects can hinder site performance and flexibility. We have to consider these factors when choosing a technology stack for our partners. Here's a closer look at some of the cons associated with a traditional WordPress build:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚡️ Plugin Overload:&lt;/strong&gt; The allure of plugins is undeniable, offering tons of features and customizations. However, this can quickly become the bane of all existence as casually adding plugin after plugin will weigh your site down. The result is a sluggish theme that not only tests the patience of your users but inevitably will affect your revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📊 The Data Dilemma:&lt;/strong&gt; WordPress's default data structure, designed to be versatile, often ends up delivering more than what's needed. It uses a bit of a catchall approach that returns all the data it has, rather than the bits and pieces you need. This overabundance of data can further strain your site's performance, making efficiency a casualty of excess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🙆 Flexibility vs. Rigidity:&lt;/strong&gt; While WordPress offers a broad canvas for development, certain creative efforts can be difficult. Were you dreaming of seamless page transitions or weaving complex animations into your site? Working this into the server-side rendered magic of PHP may create some speed bumps during development. These aspirations, though achievable, demand a deeper development effort, pushing the boundaries of WordPress's traditional setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These challenges highlight the need for a thoughtful approach to WordPress development, where the balance between functionality and performance is carefully managed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Go Headless
&lt;/h2&gt;

&lt;p&gt;Venturing beyond the traditional WordPress framework introduces us to the fairly trendy realm of the Headless CMS. Here, the frontend presentation layer is decoupled from the backend content management system. In this context, WordPress serves purely as a repository for your content, accessible via API. This separation allows developers the freedom to use any frontend technology they prefer, from React to Vue, or even HTMX. Thus, enabling the creation of dynamic, fast, and highly personalized user experiences. A Headless CMS represents a leap toward flexibility and performance, redefining how we think about content delivery and site architecture. Let’s break down the pros and cons of this approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🎉 Freedom of Tech:&lt;/strong&gt; A headless build enables you to choose the stack you want. There are a million to choose from, and a lot of times it comes down to the developer experience. When choosing a stack, however, it’s important to ensure that it’s something that will be around for more than a few months. Most clients will need that stability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏎 Speed, I Am Speed:&lt;/strong&gt; With the ability to statically generate pages, or even individual components, Headless builds have the opportunity to be faster than fast. (I made two references to the cinematic masterpiece that is Cars in this one bullet point. Impressive.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;💅 Speed AND Style:&lt;/strong&gt; Pair that speed with as many crazy page transitions and complex animations as you like. Leveraging JavaScript instead of PHP to render the front end makes all that parallax I know you want to add much easier to accomplish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚖️ Scalable by Nature:&lt;/strong&gt; Traffic spikes? Headless laughs in the face of the traffic you’ll get off that viral TikTok you made promoting it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏰 Fort Knox Level Security:&lt;/strong&gt; With less surface area for attacks, and the ability to obscure the domain used to manage your content, you’ll set yourself up for some additional security with a Headless build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🎓 Learning Curve:&lt;/strong&gt; There’s no doubt that a headless build has a bit more of a learning curve. There are necessary connections that need to be made between the frontend and the backend that go beyond the tasks in a traditional build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🤡 Setup Shenanigans:&lt;/strong&gt; Because your frontend and backend are decoupled, they end up needing to be hosted in different places (however products like[Atlas from WPEngine]are making this a bit easier). Managing code in two different places presents some unique challenges.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Making the Call
&lt;/h2&gt;

&lt;p&gt;So, which road should you take? If you're running a content-heavy site that values ease of management over everything else, traditional WordPress may be a great choice. But if you’re aiming for speed, and a tailor-made user experience, headless might suit your needs best.&lt;/p&gt;

&lt;p&gt;In the end, it's not just about choosing a CMS. It’s about choosing the right tool for the story you want to tell and the experience you want to create. Whether you stick with WordPress or go headless, the best choice is the one that aligns with the project needs, goals, and, importantly, the needs of the end users who will interact with your site.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>10 Web Dev Qualities That Actually Matter 🦄</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Thu, 28 Mar 2024 16:22:36 +0000</pubDate>
      <link>https://dev.to/magnificode/10-web-dev-qualities-that-actually-matter-1hi8</link>
      <guid>https://dev.to/magnificode/10-web-dev-qualities-that-actually-matter-1hi8</guid>
      <description>&lt;p&gt;When it comes time to build or expand a team, we often encounter the same tired songs;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How’s the culture fit?”,&lt;/p&gt;

&lt;p&gt;“What do their GitHub contributions look like?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And let’s not forget the parade of live work examples 🙄. While these elements hold some value, they often overshadow what truly energizes a development team. We’re on the lookout for individuals who are not just looking for a job but are eager to build cool shit, learn relentlessly, and dive into the depths of collaboration and shared knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Grasping the Holy Trinity
&lt;/h2&gt;

&lt;p&gt;The cornerstone for most web devs is a solid understanding of HTML, CSS, and vanilla JavaScript. This forms the bedrock upon which all other skills are built, allowing for versatility and adaptability in the face of evolving project needs. The commitment to continuous learning is what inevitably distinguishes a good developer from a great one.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Problem-Solving Skills
&lt;/h2&gt;

&lt;p&gt;The essence of development lies in tackling challenges that may seem daunting at first. The ability to dissect a problem logically and creatively is non-negotiable. But more than finding a solution, understanding the path to that solution enriches a developer, enabling them to apply these insights to future challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Communication Skills
&lt;/h2&gt;

&lt;p&gt;Clear communication helps to ensure a project moves smoothly. It’s not just about being articulate but about translating the complex language of code into narratives that stakeholders of any technical level can grasp. This skill is equally vital for effective documentation and ensuring that knowledge is accessible to anyone who may encounter the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Collaboration and Teamwork
&lt;/h2&gt;

&lt;p&gt;The magic 🪄 of development often happens in the spaces between us. The ability to work within a team, to be receptive to feedback, and to contribute to the collective knowledge pool is what propels a team forward. It’s the shared victories and learnings that forge stronger connections between you and your peers.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Curiosity and Passion for Learning
&lt;/h2&gt;

&lt;p&gt;This is the quality I search for most in people. In a field like ours, stagnation is the enemy. A genuine passion for technology and a hunger to keep abreast of the latest trends and tools are invaluable traits. They ensure that a developer not only keeps pace with the industry but also brings fresh perspectives and ideas to the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Attention to Detail
&lt;/h2&gt;

&lt;p&gt;The devil is in the details, especially in the final 10% of a project where the most intricate challenges lie. A keen eye for detail ensures that projects are not just completed, but polished to shine, embodying quality and care. All the hard work that leads to a product or project launch boils down to this last 10%. The attention to detail put in up to that point can make the final push delightful, or miserable.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Time Management and Organization
&lt;/h2&gt;

&lt;p&gt;The ability to juggle tasks 🤹‍♂️, prioritize effectively, and meet deadlines is crucial to your team’s collective success. It’s about delivering quality work within the constraints of time, without dropping a single ball.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Adaptability and Flexibility
&lt;/h2&gt;

&lt;p&gt;When thinking about both client and product work, the ability to adapt and embrace change is vital.  It’s vital to understand that a client, or the collective stakeholders in a product, may change and shift their expectations and vision. Developers must be willing to navigate these changes with grace and flexibility instead of throwing up their hands in frustration.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Creativity and Innovation
&lt;/h2&gt;

&lt;p&gt;At its heart, development is a creative pursuit. It’s about thinking outside the box to devise innovative solutions that push the boundaries of what technology can achieve, enhancing both the project and the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Empathy and User Focus
&lt;/h2&gt;

&lt;p&gt;Ultimately, the end goal of any project is to serve the user, not the client or the stakeholders. Empathy for the user’s experience, prioritizing their needs, and ensuring that the solutions we build genuinely solve problems is the hallmark of exceptional development work.&lt;/p&gt;

&lt;p&gt;As we navigate the complex terrain of hiring developers and building teams, let's shift our collective focus toward these essential attributes. It's not just about filling a position but about enriching our teams with individuals who bring more than just technical skills to the table.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you encountered these attributes in your new hires, or are there others you hold in high regard? Share your experiences and insights below. Let’s cultivate a community where knowledge and passion for development are celebrated and shared!&lt;/em&gt;**&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Five Syntax Highlighting Plugins for NextJS that aren't Terrible 🎨</title>
      <dc:creator>Dominic Magnifico</dc:creator>
      <pubDate>Thu, 14 Mar 2024 17:06:41 +0000</pubDate>
      <link>https://dev.to/magnificode/five-syntax-highlighting-plugins-for-nextjs-that-arent-terrible-32gg</link>
      <guid>https://dev.to/magnificode/five-syntax-highlighting-plugins-for-nextjs-that-arent-terrible-32gg</guid>
      <description>&lt;p&gt;As a connoisseur of artisanal tech blogs myself, I’ve found myself lost in the world of syntax highlighters, and after building our new blog here at House of Giants, I’ve found myself at a crossroads: which syntax highlighting plugin do we embrace? Fear not, for we shall explore the crème de la crème of syntax highlighting plugins, dissecting their virtues and shortcomings, and offering you a skeleton key for setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;PrismJS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PrismJS used to stand as a colossus, offering an unparalleled breadth of themes and languages. Customizability is its crown jewel, enabling you to tailor the aesthetics to your blog’s unique style.&lt;/li&gt;
&lt;li&gt;Performance-wise, it’s a lightweight contender, ensuring your blog remains quick and bloat-less.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The setup might summon a bit of a Herculean effort, particularly for those less acquainted with Webpack configs. &lt;em&gt;I despise messing with a Webpack config 😬.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Seemingly inactive. Prism v2 began development in August of 2022 but development has since seemed to stall out. The last update was almost two years ago.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup Snapshot:&lt;/strong&gt;&lt;br&gt;
From the &lt;a href="https://prismjs.com/#basic-usage" rel="noopener noreferrer"&gt;Prism Docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to use Prism with a bundler, install Prism with npm:&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;prismjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then import into your bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Prism&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prismjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make it easy to configure your Prism instance with only the languages and plugins you need, use the babel plugin, &lt;a href="https://github.com/mAAdhaTTah/babel-plugin-prismjs" rel="noopener noreferrer"&gt;babel-plugin-prismjs&lt;/a&gt;. This will allow you to load the minimum number of languages and plugins to satisfy your needs. See that plugin's documentation for configuration details.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Highlight.js&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Jack-of-All-Trades&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highlight.js is praised for its out-of-the-box readiness, and eagerness to embellish your blog without demanding much in return.&lt;/li&gt;
&lt;li&gt;Support for over 190+ languages. If it’s code, it can be highlighted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Themes exist, there are close to 500 themes to choose from, but customizing outside of those themes may be a bit cumbersome.&lt;/li&gt;
&lt;li&gt;Requires additional setup to be integrated into Markdown blogs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup Snapshot:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i highlight.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simplicity reigns. Include the Highlight.js library and desired stylesheet in your project, most simply in your &lt;code&gt;_app.js&lt;/code&gt; file, or for our Next 13+ friends, your default &lt;code&gt;layout.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight.js/styles/default.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the styles are imported you can register the languages you’d like to highlight and create a Component to apply syntax highlighting to the code within.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;hljs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight.js/lib/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;javascript&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight.js/lib/languages/javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;hljs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CodeBlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;console.log('Hello World!')&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hljs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myHtml&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/pre&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;react-syntax-highlighter&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As a native citizen of the React ecosystem, it integrates seamlessly with NextJS, like a hand fitting perfectly into a glove.&lt;/li&gt;
&lt;li&gt;Offers a broad selection of themes and the flexibility to dynamically render code blocks without the need for server-side processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its dependency on the React framework might be considered overkill for projects seeking the utmost in performance and minimalism.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup Snapshot:&lt;/strong&gt;&lt;br&gt;
Fairly similar to Highlight.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-syntax-highlighter &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new component, and import the package, along with the theme you’d like to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Prism&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SyntaxHighlighter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-syntax-highlighter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;solarizedlight&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-syntax-highlighter/dist/esm/styles/prism&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CodeExample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`function add(a, b) {
    return a + b;
  }`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SyntaxHighlighter&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;solarizedlight&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;codeString&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/SyntaxHighlighter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From &lt;a href="https://www.dhiwise.com/post/crafting-beautiful-code-blocks-with-react-syntax-highlighter" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; as well:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This example demonstrates how to import the Prism version of the syntax highlighter along with a specific style. The Prism version is optimized for JSX and other web technologies, making it a popular choice for React developers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Shiki&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilizes the same syntax highlighting engine as VS Code, promising a result that’s not just visually consistent with your development environment but also striking.&lt;/li&gt;
&lt;li&gt;Offers an impressive range of themes and the ability to extend with custom VS Code themes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shiki’s richness comes with a cost—additional setup steps that may deter those in pursuit of simplicity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup Snapshot:&lt;/strong&gt;&lt;br&gt;
Shiki provides straightforward setup instructions to highlight code passed to it via helper functions, that can then be used within React components like the others listed above. Where Shiki pulls ahead is its integrations. One I particularly like is the &lt;a href="https://shiki.matsu.io/packages/rehype" rel="noopener noreferrer"&gt;Rehype integration&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;D&lt;/span&gt; &lt;span class="nx"&gt;shiki&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;shikijs&lt;/span&gt;&lt;span class="sr"&gt;/rehyp&lt;/span&gt;&lt;span class="err"&gt;e
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, you can pare Shiki with Remark and its various modules to highlight your markdown files. I struggled with this myself, but what usually ends up having to happen, within a Next application this file will live as a helper function that converts your markdown to HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;unified&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unified&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;remarkParse&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remark-parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;remarkRehype&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remark-rehype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;rehypeStringify&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rehype-stringify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;rehypeShiki&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shikijs/rehype&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;markdownToHtml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;unified&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remarkParse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remarkRehype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rehypeShiki&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// or `theme` for a single theme&lt;/span&gt;
            &lt;span class="na"&gt;themes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitesse-light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vitesse-dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rehypeStringify&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. &lt;strong&gt;MDX&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDX transcends traditional markdown, allowing you to use JSX within your blog posts. This means you can directly leverage React components for syntax highlighting, offering an unparalleled level of flexibility and interactivity.&lt;/li&gt;
&lt;li&gt;Perfect for those who wish to blend technical writing with React-powered features, from interactive diagrams to live code editors.&lt;/li&gt;
&lt;li&gt;Next has an MDX plugin and can be easily integrated into existing Next sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With great power comes great complexity. MDX’s learning curve is steep, requiring familiarity not just with markdown, but also with React.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setup Snapshot:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://nextjs.org/docs/app/building-your-application/configuring/mdx" rel="noopener noreferrer"&gt;I’m just going to link you to the comprehensive Next documentation for this one.&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In our quest to adorn our blogs with beautifully highlighted code, the path we choose is as much about personal preference as it is about technical requirements. Each plugin offers a unique set of features, each containing unique narratives and complexities. May this guide serve as a compass, leading you to the syntax highlighting plugin that balances the annoyance of build processes and integrations.&lt;/p&gt;

&lt;p&gt;Which syntax highlighter has proven itself to you, and are there any that you believe deserve exile from our codebases? Let me know which method of syntax highlighting has proved the easiest to set up, or you can be like me and share your disdain for messing with a Webpack config. I'm fine with either 🤷&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
