<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Elijah Lewis | Writing</title><description>Essays and notes by Elijah Lewis.</description><link>https://elijahlewis.dev/</link><language>en-us</language><item><title>AI Will Change UI Development</title><link>https://elijahlewis.dev/posts/ai-will-change-ui-development/</link><guid isPermaLink="true">https://elijahlewis.dev/posts/ai-will-change-ui-development/</guid><description>I outline a possibility on how AI can dramatically change how we do frontend development through generative UI</description><pubDate>Tue, 30 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello reader,&lt;/p&gt;
&lt;p&gt;I want to make a prediction, or rather define a possibility: UI development will change. Dramatically.&lt;/p&gt;
&lt;h2&gt;New Hardware &amp;amp; The App-less Ecosystem&lt;/h2&gt;
&lt;p&gt;When Humane AI delivered its eagerly anticipated and heavily publicized launch, it presented many distinctive features — most were met with criticism. But, the most intriguing aspect to me was the lack of apps. The founders argued that because of AI, you don&apos;t need apps. Initially, I found this perplexing—wouldn&apos;t it be a very limiting experience without apps? What if developers outside the company wanted to build something for the device? How would they deliver it without an app platform? A few months later, Rabbit echoed similar sentiments at CES 2024 with its R1 device. Much of the criticism it faced was summed up by a common refrain: &quot;Why is this not just an app on my phone?&quot; This raises an even more interesting question: why are so many AI companies venturing into new hardware?&lt;/p&gt;
&lt;p&gt;The answer: access to richer inputs. An AI that can tap into your location, the surrounding noises, voices, visuals, and more can offer significantly better guidance. While your phone contains many of these sensors, it often lacks real-time environmental context — the &lt;em&gt;ambience factor&lt;/em&gt; — as it usually remains in your pocket, requiring manual operation of its camera and microphone. This has spurred the development of &lt;strong&gt;wearable computers&lt;/strong&gt; — be it necklaces, glasses, or even pins — designed to capture information seamlessly and effortlessly without user initiation.&lt;/p&gt;
&lt;p&gt;The ability to capture a categorically larger variety of inputs (audio, vision, etc.) not only enhances an AI&apos;s capabilities but also allows it to display information in a more contextually specific way. Imagine you&apos;ve been wearing a Humane AI pin for a few months while on a new diet and gym regimen. This device, having recorded all your meals and workouts, could let you ask for a display of your recent exercise data or even generate a graph showing your progress. What if you start struggling with motivation and need a gym buddy? Maybe the AI could generate profiles of others nearby for you to select a potential gym buddy.&lt;/p&gt;
&lt;p&gt;This UI would not be hard-coded but dynamic, generated and rendered based on user queries — generative UI. Consider how many traditional apps could be replaced if an AI had access to more data and could dynamically generate UIs. Hence, the app-less economy.&lt;/p&gt;
&lt;h2&gt;Services vs UI-Centric Apps&lt;/h2&gt;
&lt;p&gt;There&apos;s an important distinction I&apos;d like to draw concerning apps: there are those where the UI merely serves as a vehicle to deliver a service, and then there are those where the UI itself is the main attraction. Consider Spotify. We primarily use the app to listen to music of course, which is the service Spotify provides. Here, the UI is designed to facilitate access to this music, featuring functionalities like search, playlists, and recommendations. Now in contrast, take Instagram. The platform is highly visual; we use the app to view photos and videos, and interact with them through likes and comments. Here, the UI doesn&apos;t just support the service — it &lt;em&gt;is&lt;/em&gt; the service, as the visual and interactive elements are central to the user experience.&lt;/p&gt;
&lt;p&gt;As we&apos;ve discussed, AI could dynamically generate UI. It&apos;s conceivable that an AI could interface with Spotify&apos;s APIs to dynamically generate a user-specific UI. Imagine you&apos;re in a mall, and you hear a song you like playing in a store. You could ask your AI assistant to identify the song using a Shazam-like feature, add it to your library, and even generate a UI on your phone to show all your library songs while you&apos;re on the subway home.&lt;/p&gt;
&lt;p&gt;Consider dating apps like Hinge or Tinder, where the service provided is the opportunity to meet people and the UI is just a vehicle. Imagine an AI that knows your music tastes, dietary habits, health information, favorite shows, and hobbies. You tell your AI assistant you&apos;re interested in dating, and it finds compatible individuals in your area, checks for mutual time availability, and even sends out a generated profile to those you&apos;re interested in.&lt;/p&gt;
&lt;p&gt;For companies like Spotify and Tinder/Hinge, the shift towards AI integration would likely mean focusing less on front-end development and more on enhancing their back-end capabilities to support such AI interactions. What about Instagram? To adapt, apps that currently emphasize UI as the core of their experience would likely need to restructure themselves to become more service-oriented.&lt;/p&gt;
&lt;h2&gt;What becomes of frontend&lt;/h2&gt;
&lt;p&gt;Generative UI is already available in some fashion with &lt;a href=&quot;https://v0.dev/&quot;&gt;Vercel&apos;s v0&lt;/a&gt; and &lt;a href=&quot;https://purecode.ai/&quot;&gt;PureCode AI&lt;/a&gt; having the ability for users to describe a piece of UI they&apos;d like and AI can generate code. With this emerging technology, one might wonder if frontend development will become obsolete, particularly in companies like Spotify or Tinder that focus more on backend processes. However, replacing human workers with AI in this area would be a grave mistake.&lt;/p&gt;
&lt;p&gt;While AI can generate code, it often lacks innate creativity and may merely regurgitate existing human designs rather than produce genuine innovations. The evolving role of AI should transform, not replace, frontend development practices. AI might produce generic interfaces on its own, but these limitations can be addressed with the creative input of frontend developers.&lt;/p&gt;
&lt;p&gt;Developers could focus on creating a UI library for their apps—designing versatile, reusable components that AI systems can dynamically deploy by stitching components together. This &quot;Lego piece development&quot; approach allows developers to assemble UI elements, guided by AI&apos;s efficiency but enhanced by human creativity and insight.&lt;/p&gt;
&lt;p&gt;UI could be transmitted alongside backend API calls, a concept not new in web development. Frameworks like Rails and Laravel already facilitate the sending of HTML partials from the server to the client. React Server Components introduce a similar paradigm, but with a focus on modern, interactive web applications. This architecture allows for UI components to be dynamically adjusted in real-time. By integrating machine learning frameworks directly on the web server, the system can adapt the UI based on ongoing analysis of user data and interactions.&lt;/p&gt;
&lt;p&gt;Frontend development might increasingly focus on the infrastructure that enables AI agents to generate UIs for end users. An example of this today is &lt;a href=&quot;https://explorer.globe.engineer&quot;&gt;explorer.globe.engineer&lt;/a&gt;, a platform allowing users to query information and receive a dynamically generated, wiki-like UI. While its UI structure is consistent, with only the content changing, we can envision an AI capable of manipulating more elements to tailor the user experience further.&lt;/p&gt;
&lt;p&gt;Frontend developers might need to specialize in UX design principles, understand user behavior, and master interactive design to create templates that AI can dynamically populate. Skills in data visualization could become increasingly important, as presenting information in an instantly comprehensible and engaging way is crucial.&lt;/p&gt;
&lt;p&gt;Backend developers might shift their focus toward developing sophisticated APIs that handle data transactions and include rich metadata providing contextual descriptions of the data. For example, a music streaming service&apos;s API might describe the mood, tempo, and suitable activities (like workout or relaxation) for each track, enabling AI to create intuitive situational playlists. This extends to app-to-app communication, such as how Tinder displays a user&apos;s Spotify listening activity or Instagram posts. As AI begins to generate UIs, developers must ensure their APIs and data are well-documented contextually, allowing AI to appropriately integrate and display information from different apps.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The landscape of UI development is poised for profound transformation, though that&apos;s common in the frontend world as expectations from users only grow. But the advent of AI-driven interfaces promises not only a shift from traditional apps to more dynamic, context-aware systems but also redefines the roles of frontend and backend developers. I&apos;m personally looking forward to ways we can leverage richer inputs and more personalized interactions, leading to an app-less economy where the boundary between service and interface blurs. Of course, only time will tell whether this possibility becomes a reality.&lt;/p&gt;
</content:encoded></item><item><title>Coding as Gardening — A Journey into SaaS Productivity</title><link>https://elijahlewis.dev/posts/coding-as-gardening/</link><guid isPermaLink="true">https://elijahlewis.dev/posts/coding-as-gardening/</guid><description>Lessons learned from building a SaaS product</description><pubDate>Tue, 12 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve always been inclined towards finding the best productivity tools that fit my workflows while also spending countless hours watching productivity gurus on YouTube like Thomas Frank, Ali Abdaal, etc. to learn what the best workflows even are. Somedays, I even consider going back to bullet journaling when I miss the creative tactile pleasure of pen to paper. Of course, there are a lot of benefits you miss out on going analog over digital. But, with time I couldn&apos;t resonate with the ideas of these productivity YouTubers and didn&apos;t love my productivity tools. So, I decided I could build my own digital task manager.&lt;/p&gt;
&lt;h2&gt;Planting the Seeds: Building Modal&lt;/h2&gt;
&lt;p&gt;Wanting to build my own task manager also came at a time of wanting to build my own startup. &lt;em&gt;So&lt;/em&gt;, I thought, &lt;em&gt;why not make this my first SaaS startup?&lt;/em&gt; Enter &lt;a href=&quot;https://github.com/itsRoze/modal&quot;&gt;Modal&lt;/a&gt;, a SaaS productivity app designed to follow the Priority Matrix method while incorporating features from other beloved task apps like Things and Notion.&lt;/p&gt;
&lt;p&gt;Inspired by Herman&apos;s words in &lt;a href=&quot;https://herman.bearblog.dev/my-product-is-my-garden/&quot;&gt;My Product is my Garden&lt;/a&gt;, Modal — my newly named task manager app — would be my garden. I would tend to it routinely with care, nurturing it with precision, waiting to bear witness to the fruits it could produce.&lt;/p&gt;
&lt;p&gt;Tending to Modal was a chore. Initially, embracing the indie hacker spirit, I envisioned a swift release within a month. However, transitioning from a structured software engineering background at a large tech company, where tasks are distributed among specialized teams, posed a steep learning curve when building a product entirely on my own. Despite this, four months later, I launched a private beta. The reception was less than stellar; feedback at best was lukewarm. I decided I could do a re-write, make it sleeker, and feature-rich.&lt;/p&gt;
&lt;p&gt;I poured nearly a year into Modal before launching it on Product Hunt. The satisfaction of seeing it featured on Product Hunt was a moment of pride. The joy, however, was short-lived as the hopeful surge in sales never came to materialize. I found myself at a crossroads, grappling with technical barriers and dwindling motivation in the absence of monetary validation.&lt;/p&gt;
&lt;h2&gt;Tending to a Wilted Garden&lt;/h2&gt;
&lt;p&gt;Post-Product Hunt, I continued to toil on Modal, but the struggle became increasingly daunting. The realization hit hard—it wasn&apos;t working. The garden I envisioned, filled with thriving users, seemed more like a desolate field. Faced with this harsh truth, I made a difficult decision—I listed Modal&apos;s assets for acquisition and closed up shop.&lt;/p&gt;
&lt;p&gt;The decision to part ways with Modal wasn&apos;t easy. It was my creation, my digital haven, but the reality was that its roots weren&apos;t nourished by the commercial soil I planted it in. To clarify, I am not saying Modal shouldn&apos;t be a product for others, but its essence resonated more personally with me than it did as a commercial entity.&lt;/p&gt;
&lt;h2&gt;A New Seed: Open Sourcing the Future&lt;/h2&gt;
&lt;p&gt;I came across Linus&apos; compelling blog post on &lt;a href=&quot;https://thesephist.com/posts/tools/&quot;&gt;why you should build your own productivity tools&lt;/a&gt;. The author asserts that we shouldn&apos;t cater our workflows to tools that exist when we can instead code the tools that cater to our specific workflows. In a way, this was what I was doing with Modal — I had a very specific way of doing things, so I built an app around that. But, I tried selling it as a commercial farm rather than tending to it as my personal garden.&lt;/p&gt;
&lt;p&gt;I think I&apos;d like to make Modal open source. I&apos;ll build it for myself catered to my workflows, but others can use use it or copy it as they like. I can see a future in which, like Linus, I&apos;ll build more of my own tools and replace the services that I currently settle for. It would feel like a light touch of the gilded age of tech, where coders were mere tinkerers, learning from the code of others, directly copying it and modifying (a time before the young Bill Gates would issue copyright &lt;a href=&quot;https://jacobin.com/2018/06/github-microsoft-open-source-code-technology&quot;&gt;triggering the creation of copyleft&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;While there are many problems worth solving that could make a person a pretty penny, I&apos;ve recognized a distinction that some of those solutions are best kept...as a garden.&lt;/p&gt;
</content:encoded></item><item><title>My Self-Study Roadmap for AI</title><link>https://elijahlewis.dev/posts/my-self-study-roadmap-for-ai/</link><guid isPermaLink="true">https://elijahlewis.dev/posts/my-self-study-roadmap-for-ai/</guid><description>See my self-study roadmap for AI</description><pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Check it out&lt;/h1&gt;
&lt;p&gt;https://itsroze.notion.site/My-Self-Study-Roadmap-for-AI-187a2e42872c4a34b8f714ef71c85dc7?pvs=4&lt;/p&gt;
&lt;h1&gt;Learning AI&lt;/h1&gt;
&lt;p&gt;Back in college, I spent time studying machine learning. I&apos;ve admittedly forgotten quite a lot and then I need to learn a lot more. Based on the recommendations of friends, other people&apos;s guides, and my own searches I decided to compile the resources into a Notion page. This contains a lot of resources I think are important and that I want to study but by no means complete or perfect. It remains a work in progress. But, feel free to duplicate it and make it your own&lt;/p&gt;
</content:encoded></item><item><title>Use Astro and Solid to Build a Portfolio</title><link>https://elijahlewis.dev/posts/use-astro-and-solid-to-build-a-portfolio/</link><guid isPermaLink="true">https://elijahlewis.dev/posts/use-astro-and-solid-to-build-a-portfolio/</guid><description>Learn how to use Astro and Solid to build your portfolio</description><pubDate>Sun, 17 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Why Switch&lt;/h1&gt;
&lt;p&gt;I&apos;d previously used NextJS to host my portfolio but the problem was that I was sending a lot of Javascript to the users&apos; web browser. This is because React (which NextJS uses) needs to be loaded onto the browser for &quot;reactivity.&quot; But for a portfolio, I didn&apos;t need all that much interactivity so sending the React library to the client is heavy.&lt;/p&gt;
&lt;p&gt;That&apos;s where Astro comes into play. It lets me serve my website statically resulting in faster load times and smaller bundles. If I need interactivity, I can sprinkle that in where needed (whether it be React, Svelte, Vue, Solid, etc.).&lt;/p&gt;
&lt;p&gt;Okay but why Solid instead of React?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I&apos;ve used React for years, time to learn something new&lt;/li&gt;
&lt;li&gt;Solid is fast. SolidJS doesn&apos;t use a virtual DOM like React does where it goes node by node to see what&apos;s changed between the DOM. SolidJS instead plugs itself directly into the parts of the DOM that can be &quot;reactive.&quot; It&apos;s a substantially faster and simpler way to add reactivity to a website or app.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;What&apos;s in this Guide&lt;/h1&gt;
&lt;p&gt;This isn&apos;t quite a full &quot;follow along&quot; type tutorial. I&apos;m going to give you the gist to understand at a high level and if you want to see my source code, you can check it out &lt;a href=&quot;https://github.com/itsRoze/personal&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;The Tech Stack&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Astro for the main framework&lt;/li&gt;
&lt;li&gt;SolidJS to sprinkle some reactive components as needed&lt;/li&gt;
&lt;li&gt;TailwindCSS because it lets us quickly style our website with easy to remember utility classes&lt;/li&gt;
&lt;li&gt;Vercel to deploy&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Let&apos;s Get Started&lt;/h1&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;Create a new Astro project&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm create astro@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add TailwindCSS&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm astro add tailwind
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add SolidJS&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm astro add solid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re wondering why pnpm, it&apos;s because its a whole heck faster.&lt;/p&gt;
&lt;p&gt;Now run your project&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pages&lt;/h2&gt;
&lt;p&gt;Creating pages in Astro is simple. Within the &lt;code&gt;pages&lt;/code&gt; directory you can create a new route e.g. &lt;code&gt;about.astro&lt;/code&gt; which would yield &lt;code&gt;portfolio.com/about&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since I also want to have a blog, I can create markdown files in the pages directory. Particularly subdirectories will yield nested routes e.g. &lt;code&gt;portfolio.com/blog/post-1&lt;/code&gt; if I create a file &lt;code&gt;pages/blog/post-1.md&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Layouts&lt;/h2&gt;
&lt;p&gt;Pages in my portfolio have a general layout and Astro allows you to create layouts that you can wrap your content within.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import &quot;@styles/global.css&quot;;
import Head from &quot;@components/Head.astro&quot;;
import { Navigation } from &quot;@components/navigation/navigation.tsx&quot;;

interface Props {
  title: string;
  ...
}

const { title, ... } = Astro.props;
---

&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;Head
    title={title}
    description={description}
    type={type}
    image={image}
    date={date}
  /&amp;gt;
  &amp;lt;body class={background}&amp;gt;
    &amp;lt;Navigation
      client:load
      textColor={navColor}
      underlineColor={navUnderline}
      pathname={Astro.url.pathname}
    /&amp;gt;
    &amp;lt;main&amp;gt;&amp;lt;slot /&amp;gt;&amp;lt;/main&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This layout file has two components &lt;code&gt;Head&lt;/code&gt; and &lt;code&gt;Navigation&lt;/code&gt; defined elsewhere that we&apos;ll talk about later.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;slot&lt;/code&gt; element is where my page content will go (a sort of placeholder).&lt;/p&gt;
&lt;p&gt;I can use this layout as such:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
---
import Layout from &quot;../layouts/Layout.astro&quot;;
---

&amp;lt;Layout
  title=&quot;ROZE | ABOUT&quot;
  ...
&amp;gt;
  &amp;lt;header&amp;gt;
      &amp;lt;h1&amp;gt;
        Hello. I am a Washington D.C. based software engineer 🧑🏽‍💻.
      &amp;lt;/h1&amp;gt;
  &amp;lt;/header&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Components&lt;/h1&gt;
&lt;p&gt;In the above example, you saw that I used some components. You can use Astro syntax to make components or you can also make them in another framework like Solid. Components can take in props like we&apos;re used to and are reusable.&lt;/p&gt;
&lt;p&gt;Since the navbar would be on each page, I had it as a component within my Layout file.&lt;/p&gt;
&lt;p&gt;I know I want a list of my projects on my portfolio so I can make this a component.&lt;/p&gt;
&lt;p&gt;I can create &lt;code&gt;src/components/projects.tsx&lt;/code&gt; to make a solid component.&lt;/p&gt;
&lt;p&gt;Let&apos;s store some example projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import modalImg from &quot;../images/projects/modal.png&quot;;
import giftableImg from &quot;../images/projects/giftable.png&quot;;

const projects = [
  {
    title: &quot;Modal&quot;,
    description:
      &quot;My first SaaS app. I never found a great productivity manager, so I built an opinionated one. Modal was designed with simplicity for the everyday user&quot;,
    year: &quot;2023-Present&quot;,
    url: &quot;https://usemodal.com/&quot;,
    image: modalImg,
    tags: [&quot;React&quot;, &quot;NextJS&quot;, &quot;Tailwind CSS&quot;, &quot;React Native&quot;, &quot;AWS&quot;, &quot;SQL&quot;],
  },
  {
    title: &quot;Giftable&quot;,
    description:
      &quot;I created Giftable so that I could remember gift ideas for friends and family, as well as myself :) I thought building a social network would be a great learning experience to up my code skills too&quot;,
    year: &quot;2022-2023&quot;,
    url: &quot;https://giftableapp.com/&quot;,
    image: giftableImg,
    tags: [&quot;React&quot;, &quot;NextJS&quot;, &quot;Tailwind CSS&quot;, &quot;SQL&quot;],
  },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll notice here that I&apos;m importing images within my &lt;code&gt;src&lt;/code&gt; folder. This is because Astro can bundle and optimize them versus if stored in the &lt;code&gt;public/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Now I can display these projects in a Solid component which has similar syntax to React but with a few differences&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const ProjectList: Component = () =&amp;gt; {
  return (
    &amp;lt;div class=&quot;w-full xl:h-[30rem] h-[25rem] overflow-y-scroll custom-scroll&quot;&amp;gt;
      &amp;lt;For each={projects}&amp;gt;
        {(project) =&amp;gt; (
          &amp;lt;FadeIn&amp;gt;
            &amp;lt;div class=&quot;xl:p-10 px-10 py-4 flex flex-col lg:flex-row items-center h-full justify-between&quot;&amp;gt;
              &amp;lt;div class=&quot;flex flex-col items-center lg:w-1/2 w-full&quot;&amp;gt;
                &amp;lt;h2 class=&quot;lg:text-6xl text-4xl font-light py-2 text-center&quot;&amp;gt;
                  &amp;lt;a href={project.url} target=&quot;_blank&quot; class=&quot;hover:underline&quot;&amp;gt;
                    {project.title}
                  &amp;lt;/a&amp;gt;
                &amp;lt;/h2&amp;gt;
                &amp;lt;div class=&quot;w-4/5 pb-10&quot;&amp;gt;
                  &amp;lt;img
                    src={project.image.src}
                    class=&quot;w-full h-auto shadow-xl shadow-gray-800 rounded-lg&quot;
                  /&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;div class=&quot;flex flex-col lg:w-1/2 w-full lg:gap-10 gap-4&quot;&amp;gt;
                &amp;lt;p class=&quot;lg:text-4xl lg:text-left text-center text-2xl font-extralight &quot;&amp;gt;
                  {project.description}
                &amp;lt;/p&amp;gt;
                &amp;lt;div class=&quot;text-center lg:text-left&quot;&amp;gt;
                  &amp;lt;For each={project.tags}&amp;gt;
                    {(tag) =&amp;gt; (
                      &amp;lt;span class=&quot;bg-yellow-200 w-fit px-2 py-0.5 rounded leading-4 mr-1 mb-1 inline-flex font-medium&quot;&amp;gt;
                        {tag}
                      &amp;lt;/span&amp;gt;
                    )}
                  &amp;lt;/For&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;p class=&quot;lg:text-3xl lg:text-left text-center text-2xl font-medium&quot;&amp;gt;
                  {project.year}
                &amp;lt;/p&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/FadeIn&amp;gt;
        )}
      &amp;lt;/For&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things about this code&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We&apos;re using TailwindCSS to style our elements&lt;/li&gt;
&lt;li&gt;These are actually HTML elements unlike React so you&apos;ll see we&apos;re using &lt;code&gt;class&lt;/code&gt; instead of &lt;code&gt;className&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;For&lt;/code&gt; component allows us to render a list and is already keyed which is why we&apos;re not using the &lt;code&gt;key&lt;/code&gt; attribute like you would in React if you were mapping an array&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&apos;s the code for the &lt;code&gt;FadeIn&lt;/code&gt; component that I&apos;m using&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface IFadeIn {
  children: JSX.Element | JSX.Element[];
}

const FadeIn: Component&amp;lt;IFadeIn&amp;gt; = (props) =&amp;gt; {
  const [visible, setVisible] = createSignal(false);
  let domRef: HTMLDivElement | undefined;

  createEffect(() =&amp;gt; {
    const observer = new IntersectionObserver((entries) =&amp;gt; {
      entries.forEach((entry) =&amp;gt; setVisible(entry.isIntersecting));
    });
    observer.observe(domRef as Element);
  });

  return (
    &amp;lt;div
      ref={domRef}
      class={`fade-in-section ${visible() ? &quot;is-visible&quot; : &quot;&quot;}`}
    &amp;gt;
      {props.children}
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main thing to note here is &lt;code&gt;createSignal&lt;/code&gt;. This is effectively like &lt;code&gt;useState&lt;/code&gt;. But remember how I said SolidJS plugs itself into the actual DOM whereas React uses a virtual DOM? Because of that, &lt;code&gt;visible&lt;/code&gt; isn&apos;t a static variable like in React. It&apos;s a getter method, meaning we have to call it as &lt;code&gt;visible()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another thing to note, &lt;code&gt;createEffect&lt;/code&gt; is like &lt;code&gt;useEffect&lt;/code&gt; but we don&apos;t need to supply it a dependency array like in React because Solid is smart enough to detect the signals being used.&lt;/p&gt;
&lt;p&gt;Finally, I can use my Solid component in my Astro page, &lt;code&gt;pages/projects.astro&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import Layout from &quot;../layouts/Layout.astro&quot;;
import &quot;@styles/projects.css&quot;;
import { ProjectList } from &quot;@components/projects&quot;;
---
&amp;lt;Layout
  title=&quot;ROZE | PROJECTS&quot;
  ...
&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h1&amp;gt;
      Featured Projects
    &amp;lt;/h1&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;ProjectList client:load /&amp;gt;
  &amp;lt;/section&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, Astro won&apos;t send the component&apos;s javascript to the client, so we have to specify the directive &lt;code&gt;client:load&lt;/code&gt; to tell Astro to do so! What&apos;s awesome about this is I could essentially use Solid (or another UI framework) instead of a mix of Astro and Solid and Astro will only send the HTML by default.&lt;/p&gt;
&lt;h1&gt;Blog Posts&lt;/h1&gt;
&lt;p&gt;So now how do I generate 1) a page to display all my blogs posts and 2) a page to show the content of a single post?&lt;/p&gt;
&lt;p&gt;On &lt;code&gt;pages/blog.astro&lt;/code&gt; for example we can write Javascript within the &lt;code&gt;---&lt;/code&gt; snippet to retrieve all our markdown files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import Layout from &quot;@layouts/Layout.astro&quot;;
import BlogCard from &quot;@components/BlogCard.astro&quot;;
export interface Frontmatter {
  title: string;
  excerpt: string;
  coverImage: string;
  ogImage: string;
  date: string;
}
const allPosts = await Astro.glob&amp;lt;Frontmatter&amp;gt;(&quot;./*.md&quot;);
//sort by date
allPosts.sort((a, b) =&amp;gt; {
  const dateA = new Date(a.frontmatter.date);
  const dateB = new Date(b.frontmatter.date);
  return dateB.getTime() - dateA.getTime();
});
---
&amp;lt;Layout
  title=&quot;ROZE | WRITING&quot;
  ...
&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h1&amp;gt;Blog&amp;lt;/h1&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;ul&amp;gt;
      {
        allPosts.map((post) =&amp;gt; (
          &amp;lt;li&amp;gt;
            &amp;lt;BlogCard
              url={post.url ?? &quot;/404&quot;}
              frontmatter={post.frontmatter}
              rawContent={post.rawContent()}
            /&amp;gt;
          &amp;lt;/li&amp;gt;
        ))
      }
    &amp;lt;/ul&amp;gt;
  &amp;lt;/section&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Astro.glob&lt;/code&gt; will import many files at once (in this case all our markdown files within the current folder). We use Typescript to define what the frontmatter will look like, e.g.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
layout: &quot;../../layouts/MarkdownLayout.astro&quot;
title: What is a Tech Cooperative - A Short Introduction
excerpt: Learn what a tech cooperative is, how it works, and why it&apos;s the next big thing in tech.
coverImage: /images/blog/cover-what-is-a-tech-cooperative.png
ogImage: /images/blog/cover-what-is-a-tech-cooperative.png
date: &quot;2023-04-18&quot;
---

&amp;gt; The most tragic form of loss isn’t the loss of security; it’s the loss of the capacity to imagine that things could be different.
&amp;gt;
&amp;gt; &amp;amp;mdash; &amp;lt;cite&amp;gt;Ernest Bloch, The Principle of Hope&amp;lt;/cite&amp;gt;

# What&apos;s a Cooperative?

...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the frontmatter matches the typescript interface we defined, but what about &lt;code&gt;MarkdownLayout&lt;/code&gt;. I created another layout file to define the layout of a blog post. &lt;code&gt;BlogCard&lt;/code&gt; is a component I created to render a url to each blog post with some information about that post.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;MarkdownLayout&lt;/code&gt; we can pull in the props passeed and slot in our blog post&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import type { Frontmatter } from &quot;@/pages/writing/index.astro&quot;;
import Layout from &quot;./Layout.astro&quot;;
import readingTime from &quot;reading-time&quot;;
interface Props {
  frontmatter: Frontmatter;
  rawContent: () =&amp;gt; string;
}
const { frontmatter, rawContent } = Astro.props;
const { title, excerpt, ogImage, date } = frontmatter;
---
&amp;lt;Layout
  title={title}
  ...
&amp;gt;
  &amp;lt;header class=&quot;px-1&quot;&amp;gt;
    &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;
    &amp;lt;div class=&quot;flex items-center gap-2 md:text-lg font-normal text-zinc-500&quot;&amp;gt;
      &amp;lt;div
        class=&quot;flex grow flex-col sm:flex-row sm:items-center sm:justify-between mt-4&quot;
      &amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;span&amp;gt;Roze&amp;lt;/span&amp;gt; /{&quot; &quot;}
          &amp;lt;time&amp;gt;
            {
              new Date(date).toLocaleDateString(&quot;en-us&quot;, {
                year: &quot;numeric&quot;,
                month: &quot;short&quot;,
                day: &quot;numeric&quot;,
              })
            }
          &amp;lt;/time&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class=&quot;flex items-center gap-1&quot;&amp;gt;
          &amp;lt;span&amp;gt;{readingTime(rawContent()).text}&amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;article class=&quot;prose max-w-screen-xl md:prose-xl px-1 lg:px-0 lg:mt-8&quot;&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/Layout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;My Review&lt;/h1&gt;
&lt;p&gt;Overall, it was really fast to learn Astro especially if you&apos;ve used other frameworks like NextJS before. Astro&apos;s documentation is well written and I even recommend their tutorial.&lt;/p&gt;
&lt;p&gt;The developer experience was great, I had a fun time building and very little issues.&lt;/p&gt;
&lt;p&gt;But what about performance? So it would be unfair to directly compare my revamped portfolio to my old one because I changed quite a bit. But a few general things to note&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network requests are down by almost 4x. In my NextJS website, I would have close to 40 requests on some pages. Astro will send 10&lt;/li&gt;
&lt;li&gt;Loading times were all mostly down, with some down by close to 100ms.&lt;/li&gt;
&lt;li&gt;Network resources substantially lowered (over a MB with NextJS, versus a few kB with Astro)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, adding &lt;code&gt;&amp;lt;ViewTransition /&amp;gt;&lt;/code&gt; that Astro offers added a smooth app like feeling with just that one line!&lt;/p&gt;
&lt;p&gt;What about the bad? I have to test this more, but some images (even using Astro&apos;s image optimization) loads slow. This could be an issue with Vercel though. More investigation is needed.&lt;/p&gt;
</content:encoded></item><item><title>Rebuilding my Portfolio with Next, MDX, and Contentlayer</title><link>https://elijahlewis.dev/posts/rebuilding-my-portfolio/</link><guid isPermaLink="true">https://elijahlewis.dev/posts/rebuilding-my-portfolio/</guid><description>Learn how to use MDX with NextJS and Contentlayer</description><pubDate>Mon, 21 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Why I Stopped Using Ghost&lt;/h1&gt;
&lt;p&gt;I liked the idea of opening up my iPad, sipping on a caramel latte in an overly-hipster Brooklyn cafe, writing a new tech post. Ghost CMS was my way to do that (&lt;a href=&quot;https://dev.to/emotionaldaffodil/building-a-blog-with-nextjs-and-ghost-5ehc&quot;&gt;see my setup&lt;/a&gt;). It was, however, expensive ever since Heroku broke up with us and I moved onto Digital Ocean which is $6 month. But also, sometimes Ghost would crash and I didn&apos;t want to spend too long debugging when redeploying quickly fixed whatever was broken.&lt;/p&gt;
&lt;p&gt;Ultimately, crashes and money didn&apos;t warrant a ridiculous aesthetic of writing in a cafe because I never actually did it. Caramel lattes are also expensive.&lt;/p&gt;
&lt;p&gt;And I can also use Obsidian, my markdown notetaker, and then just copy that to my blog, achieving all of this for free.&lt;/p&gt;
&lt;h1&gt;Technologies&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/&quot;&gt;Next JS&lt;/a&gt; -- my favorite full stack framework&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; -- because I don&apos;t know how to do CSS otherwise&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/docs/advanced-features/using-mdx&quot;&gt;MDX&lt;/a&gt; -- to use React within my markdown (probably won&apos;t use much JSX, but hey why not at least have it)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.contentlayer.dev/&quot;&gt;Contentlayer&lt;/a&gt; -- transform the mdx posts into type-safe json data&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vercel.com/home&quot;&gt;Vercel&lt;/a&gt; -- deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Getting Started&lt;/h1&gt;
&lt;p&gt;I&apos;ve started using the T3 CLI to make my apps these days because the stack generally is one I enjoy and I love the cohesion together.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm create t3-app@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only select Tailwind, we don&apos;t need the other packages&lt;/p&gt;
&lt;p&gt;After installation, we can clear up the homepage&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { type NextPage } from &quot;next&quot;;
import Head from &quot;next/head&quot;;
const Home: NextPage = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Create T3 App&amp;lt;/title&amp;gt;
        &amp;lt;meta name=&quot;description&quot; content=&quot;Generated by create-t3-app&quot; /&amp;gt;
        &amp;lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;main className=&quot;flex min-h-screen flex-col items-center bg-gradient-to-b from-[#2e026d] to-[#15162c] pt-20&quot;&amp;gt;
        &amp;lt;h1 className=&quot;text-7xl font-bold text-white&quot;&amp;gt;My Cool Blog&amp;lt;/h1&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default Home;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Configuring MDX&lt;/h1&gt;
&lt;p&gt;To be able to write &lt;code&gt;.mdx&lt;/code&gt; files, we&apos;ll need a few plugins&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@next/mdx -- to use with Next&lt;/li&gt;
&lt;li&gt;@mdx-js/loader -- required package of @next/mdx&lt;/li&gt;
&lt;li&gt;@mdx-js/react -- required package of @next/mdx&lt;/li&gt;
&lt;li&gt;gray-matter -- to ignore frontmatter from rendering&lt;/li&gt;
&lt;li&gt;rehype-autolink-headings -- allows to add links to headings with ids on there already&lt;/li&gt;
&lt;li&gt;rehype-slug -- allows to add links to headings for documents that don&apos;t already have ids&lt;/li&gt;
&lt;li&gt;rehype-pretty-code -- makes code pretty with syntax highlighting, line numbers, etc&lt;/li&gt;
&lt;li&gt;remark-frontmatter -- plugin to support frontmatter&lt;/li&gt;
&lt;li&gt;shiki -- coding themes we can use for rendering code snippets&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;yarn add @next/mdx @mdx-js/loader @mdx-js/react gray-matter rehype-autolink-headings rehype-slug rehype-pretty-code remark-frontmatter shiki
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Setting Up Contentlayer&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.contentlayer.dev/&quot;&gt;Contentlayer&lt;/a&gt; makes it super easy to grab our mdx blog posts in a type-safe way.&lt;/p&gt;
&lt;p&gt;First install it and its associated Next js plugin&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn add contentlayer next-contentlayer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify your &lt;code&gt;next.config.mjs&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// next.config.mjs

import { withContentlayer } from &quot;next-contentlayer&quot;;

/** @type {import(&apos;next&apos;).NextConfig} */
const nextConfig = {
  // Configure pageExtensions to include md and mdx
  pageExtensions: [&quot;ts&quot;, &quot;tsx&quot;, &quot;js&quot;, &quot;jsx&quot;, &quot;md&quot;, &quot;mdx&quot;],
  reactStrictMode: true,
  swcMinify: true,
};

// Merge MDX config with Next.js config
export default withContentlayer(nextConfig);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modify your &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
      &quot;contentlayer/generated&quot;: [&quot;./.contentlayer/generated&quot;]
    }
  },
  &quot;include&quot;: [&quot;next-env.d.ts&quot;, &quot;**/*.tsx&quot;, &quot;**/*.ts&quot;, &quot;.contentlayer/generated&quot;]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a file &lt;code&gt;contentlayer.config.ts&lt;/code&gt; and we will do three things&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define the schema of our Post and where the content lives&lt;/li&gt;
&lt;li&gt;Setup our remark and rehype plugins&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import { defineDocumentType, makeSource } from &quot;contentlayer/source-files&quot;;
import rehypeAutolinkHeadings from &quot;rehype-autolink-headings&quot;;
import rehypePrettyCode from &quot;rehype-pretty-code&quot;;
import rehypeSlug from &quot;rehype-slug&quot;;
import remarkFrontmatter from &quot;remark-frontmatter&quot;;

export const Post = defineDocumentType(() =&amp;gt; ({
  name: &quot;Post&quot;,
  filePathPattern: `**/*.mdx`,
  contentType: &quot;mdx&quot;,
  fields: {
    title: {
      type: &quot;string&quot;,
      description: &quot;The title of the post&quot;,
      required: true,
    },
    excerpt: {
      type: &quot;string&quot;,
      description: &quot;The excerpt of the post&quot;,
      required: true,
    },
    date: {
      type: &quot;string&quot;,
      description: &quot;The date of the post&quot;,
      required: true,
    },
    coverImage: {
      type: &quot;string&quot;,
      description: &quot;The cover image of the post&quot;,
      required: false,
    },
    ogImage: {
      type: &quot;string&quot;,
      description: &quot;The og cover image of the post&quot;,
      required: false,
    },
  },
  computedFields: {
    url: {
      type: &quot;string&quot;,
      resolve: (post) =&amp;gt; `/blog/${post._raw.flattenedPath}`,
    },
    slug: {
      type: &quot;string&quot;,
      resolve: (post) =&amp;gt; post._raw.flattenedPath,
    },
  },
}));

const prettyCodeOptions = {
  theme: &quot;material-theme-palenight&quot;,

  onVisitLine(node: { children: string | unknown[] }) {
    if (node.children.length === 0) {
      node.children = [{ type: &quot;text&quot;, value: &quot; &quot; }];
    }
  },

  onVisitHighlightedLine(node: { properties: { className: string[] } }) {
    node.properties.className.push(&quot;highlighted&quot;);
  },

  onVisitHighlightedWord(node: { properties: { className: string[] } }) {
    node.properties.className = [&quot;highlighted&quot;, &quot;word&quot;];
  },
};

export default makeSource({
  contentDirPath: &quot;content&quot;,
  documentTypes: [Post],
  mdx: {
    remarkPlugins: [remarkFrontmatter],
    rehypePlugins: [
      rehypeSlug,
      [rehypeAutolinkHeadings, { behavior: &quot;wrap&quot; }],
      [rehypePrettyCode, prettyCodeOptions],
    ],
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re using git, don&apos;t forget to add the generated content to your &lt;code&gt;gitignore&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# contentlayer
.contentlayer
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Add Post Content&lt;/h1&gt;
&lt;p&gt;Create a folder called &lt;code&gt;content&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Create a file in &lt;code&gt;content&lt;/code&gt; called &lt;code&gt;first-post.mdx&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: First Post
excerpt: My first ever post on my blog
date: &quot;2022-02-16&quot;
---

# Hello World

My name is Roze and I built this blog to do cool things

- Like talking about pets
- And other cool stuff

## Random Code

```mdx {1,15} showLineNumbers title=&quot;Page.mdx&quot;
import { MyComponent } from &quot;../components/...&quot;;

# My MDX page

This is an unordered list

- Item One
- Item Two
- Item Three

&amp;lt;section&amp;gt;And here is _markdown_ in **JSX**&amp;lt;/section&amp;gt;

Checkout my React component

&amp;lt;MyComponent /&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you&apos;ve created a new post, make sure to run your app to trigger contentlayer to generate&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see a new folder called &lt;code&gt;.contentlayer&lt;/code&gt; which will have a &lt;code&gt;generated&lt;/code&gt; folder that defines your schemas and types.&lt;/p&gt;
&lt;h1&gt;Display All Blog Posts&lt;/h1&gt;
&lt;p&gt;We can use &lt;code&gt;getStaticProps&lt;/code&gt; to pull data from our &lt;code&gt;content&lt;/code&gt; folder because contentlayer provides us with &lt;code&gt;allPosts&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { allPosts } from &quot;../../.contentlayer/generated&quot;;
import { type GetStaticProps } from &quot;next&quot;;
...
export const getStaticProps: GetStaticProps = () =&amp;gt; {
  const posts = allPosts.sort(
    (a, b) =&amp;gt; new Date(b.date).getTime() - new Date(a.date).getTime()
  );

  return {
    props: {
      posts,
    },
  };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then update the component to show these posts&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Props {
  posts: Post[];
}

const Home: NextPage&amp;lt;Props&amp;gt; = ({ posts }) =&amp;gt; {
  r
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item></channel></rss>