<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Jake Prins</title>
        <link>https://jakeprins.com</link>
        <description>Your blog description</description>
        <lastBuildDate>Thu, 23 Apr 2026 12:29:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Jake Prins</title>
            <url>https://jakeprins.com/favicon.ico</url>
            <link>https://jakeprins.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Mindlumen, the most capable AI therapist, is available on iOS!]]></title>
            <link>https://jakeprins.com/blog/mindlumen-ai-therapist-mobile-app</link>
            <guid>https://jakeprins.com/blog/mindlumen-ai-therapist-mobile-app</guid>
            <pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[MindLumen launches on iOS, bringing an innovative AI therapist that learns and adapts to provide personalized mental health support, available 24/7.]]></description>
            <content:encoded><![CDATA[<p>Today, we&#x27;re launching the mobile app of MindLumen!</p>
<p>We&#x27;ve poured months into crafting what I believe is the best AI therapist out there.</p>
<a href="https://mindlumen.ai" target="_blank" rel="noopener noreferrer"><img alt="Mindlumen AI Therapist" src="/blog/mindlumen-mobile.png" class="w-full"/></a>
<p>Unlike many other apps, this isn&#x27;t just another AI wrapper. From day one, we realized that an AI designed to act as a therapist would require its own fine-tuned model that could learn from you over time.</p>
<p>We needed an AI that could be conversational and supportive, capable of asking the right questions to lead to new insights rather than jumping to conclusions. We spent a lot of effort refining and tweaking it, working with respected psychologists to ensure its effectiveness. I continue to be blown away by the results.</p>
<p>Now that&#x27;s available as a mobile app, you&#x27;ll have a companion to talk to about anything on your mind—whether through chat or voice. Plus, it gets way better over time. The longer you use it, the better it gets to know you and it can support you.</p>
<p>While some may find it strange or believe that people should only visit human therapists, AI therapy will become increasingly normalized. There are several reasons for this:</p>
<ol>
<li>
<p>Shortage of human professionals: Many people face long waiting times before they can get help. AI therapists are available 24/7, whenever you need them.</p>
</li>
<li>
<p>Affordability: Human therapists can be costly, making them inaccessible for some. AI therapy is very affordable.</p>
</li>
<li>
<p>Overcoming barriers: For many, it is scary to take the first step of talking to a stranger about their problems. An AI therapist doesn&#x27;t judge and is more approachable.</p>
</li>
</ol>
<a href="https://apps.apple.com/nl/app/mindlumen-ai-therapist/id6738006220" target="_blank" rel="noopener noreferrer"><img alt="Mindlumen AI Therapist" src="/blog/mindlumen-mobile-2.png" class="w-full rounded-lg"/></a>
<p>Will AI fully replace human therapists? No, it will not replace that human connection. But it does have its place, and it continues to improve. Many people find it beneficial to chat with an AI regularly about their thoughts and emotions.</p>
<p>For more serious mental health concerns, seeking help from a professional is crucial. Never solely rely on an AI. Our app and the AI itself will continue to remind you of that. However, for many, AI can serve as a valuable complement to human therapy.</p>
<p>Many people facing everyday challenges may not feel the need to see a human therapist. We all go through tough times. Nearly half of all people will encounter some form of mental well-being challenge in their lives, and one in four will experience it each year. An AI companion that listens, provides clarity, and encourages a healthy lifestyle and social connections can be quite helpful for many people.</p>
<p>While it may not be for everyone, it&#x27;s already making an impact on many. I&#x27;m excited that, with the launch of the mobile app, support is now more accessible than ever!</p>
<p>Download it for free in the App Store <a href="https://apps.apple.com/nl/app/mindlumen-ai-therapist/id6738006220">here</a>.</p>
<p>See the original Tweet of this post <a href="https://x.com/jake_prins/status/1892851016598430169">here</a>.</p>
<a href="https://apps.apple.com/nl/app/mindlumen-ai-therapist/id6738006220" target="_blank" rel="noopener noreferrer"><img alt="Mindlumen AI Therapist" src="/blog/mindlumen-mobile-3.png" class="w-full"/></a>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Supabase vs Firebase: Choosing the Right Backend for Your Next Project]]></title>
            <link>https://jakeprins.com/blog/supabase-vs-firebase-2024</link>
            <guid>https://jakeprins.com/blog/supabase-vs-firebase-2024</guid>
            <pubDate>Sat, 02 Nov 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[A comparison of Supabase and Firebase, two popular backend services for web and mobile app development.]]></description>
            <content:encoded><![CDATA[<p>As someone who&#x27;s been a developer for over 10 years and now works full-time on my own projects, I&#x27;ve had the opportunity to explore various backend services. A few years ago, I created and sold a Firebase boilerplate called Serverless SaaS, which helped many developers kickstart their projects with Firebase.</p>
<p>However, over time, I&#x27;ve found myself gravitating towards <strong>Supabase</strong> for my newer projects. Over time I created a Supabase starter called <a href="https://www.supanext.com">Supanext</a> to help myself and other developers kickstart AI and SaaS projects with Supabase &amp; Nextjs. In this post, I&#x27;d like to share my experiences with both Firebase and Supabase to help you decide which one might be the best fit for your next project.</p>
<blockquote>
<p><strong>For Mobile Developers</strong>: If you&#x27;re specifically building mobile apps, there&#x27;s also <a href="https://calljmp.com/">Calljmp</a> - a secure backend for mobile apps built on Cloudflare&#x27;s edge infrastructure.</p>
</blockquote>
<img alt="Supabase vs Firebase" srcSet="/_next/image?url=https%3A%2F%2Fdemo.supanext.com%2Fblog%2Fcomparing-supabase-and-firebase-for-your-next-project.webp&amp;w=750&amp;q=75 1x, /_next/image?url=https%3A%2F%2Fdemo.supanext.com%2Fblog%2Fcomparing-supabase-and-firebase-for-your-next-project.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=https%3A%2F%2Fdemo.supanext.com%2Fblog%2Fcomparing-supabase-and-firebase-for-your-next-project.webp&amp;w=1920&amp;q=75" width="700" height="400" decoding="async" data-nimg="1" class="rounded-lg shadow-md" loading="lazy" style="color:transparent"/>
<h2>What is Firebase?</h2>
<p><a href="https://firebase.google.com/">Firebase</a> is a comprehensive Backend-as-a-Service (BaaS) platform owned by Google. It started as a real-time database and has evolved into a suite of tools aimed at mobile and web developers. Some of its key features include:</p>
<ul>
<li><strong>Firestore Database</strong>: A NoSQL, document-oriented database that stores data in flexible, JSON-like documents.</li>
<li><strong>Authentication</strong>: Easy-to-use user authentication supporting email/password, phone numbers, and social logins.</li>
<li><strong>Cloud Functions</strong>: Serverless functions that run backend code in response to events triggered by Firebase features or HTTPS requests.</li>
<li><strong>Cloud Storage</strong>: Secure file uploads and downloads for your apps.</li>
<li><strong>Hosting</strong>: Fast and secure web hosting for static and dynamic content.</li>
</ul>
<h3>My experience with Firebase</h3>
<p>When I first started using Firebase, I was impressed by how quickly I could get a simple project off the ground. However, over time, I struggled with Firestore. I have been developing with Ruby on Rails for several years and got used to using a relational database like PostgreSQL. When I first tried Firestore, it was easy for a simple side project, but when building something more complex, I constantly thought about performance and how to design my data. I know you can build scalable apps with Firestore—there are plenty of examples out there, and it&#x27;s widely popular—but it never truly felt natural to me. If you are accustomed to relational databases like PostgreSQL, be prepared to feel a bit uncomfortable with Firestore at first.</p>
<p>When it comes to Cloud functions, they perform well, and the developer tools are excellent once you understand how to use them. However, it may take some initial time to develop a good workflow. Especially compared to how straightforward Vercel has made it with Next.js, where you can simply create an API route like /api/my-function, and your serverless lambda will be up and running as soon as you push to Git.
Still Cloud Functions work great too. Very comparable experience with Supabase Edge Functions.</p>
<img alt="Firebase" srcSet="/_next/image?url=%2Fblog%2Ffirebase-2.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2Fblog%2Ffirebase-2.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2Fblog%2Ffirebase-2.png&amp;w=1920&amp;q=75" width="700" height="400" decoding="async" data-nimg="1" class="rounded-lg shadow-md transition-shadow duration-300 hover:shadow-lg" loading="lazy" style="color:transparent"/>
<h2>What is Supabase?</h2>
<p><a href="https://supabase.com/">Supabase</a> is an open-source alternative to Firebase that I&#x27;ve grown to appreciate deeply. It&#x27;s built on top of <strong>PostgreSQL</strong>, a powerful and scalable relational database. Supabase offers:</p>
<ul>
<li><strong>PostgreSQL Database</strong>: A relational database with SQL support, ideal for structured data and complex queries.</li>
<li><strong>Authentication</strong>: Built-in user authentication with support for various providers like email, OAuth, and third-party logins.</li>
<li><strong>Realtime Subscriptions</strong>: Listen to database changes in real-time and stream updates directly to your application.</li>
<li><strong>Storage</strong>: Securely manage and serve large files like images and videos.</li>
<li><strong>Edge Functions</strong>: Deploy serverless functions globally for custom backend logic.</li>
</ul>
<h3>My experience with Supabase</h3>
<p>What drew me to Supabase was its developer-friendly tools, comprehensive documentation, and intuitive UI. As someone who feels more comfortable with relational databases, using PostgreSQL with Supabase just felt right.</p>
<p>Authentication is made super easy with Supabase. They provide excellent SDKs and helpful guides that enable you to set things up quickly. One of the things I really appreciate about the Supabase tools is how well they work together. For example, you can use the Auth SDK to get the current user and then use the Storage SDK to upload a profile picture. Additionally, with database functions, you can ensure that whenever a new user signs up, a profile record is created for extra details.</p>
<p>Especially with the integration of AI, you can ask it to assist you by providing the SQL you need, which you can simply copy into the SQL editor and run. Just make sure to double-check everything, as AI is still making some mistakes; however, it is improving continuously. If you prefer not to use SQL, you can take advantage of the user-friendly editor to create your tables and columns easily.</p>
<p>Overall, I found that I was able to move much quicker with Supabase. It’s a matter of preference, but I find the UI very sleek and I prefer it over Firebase.</p>
<a href="https://www.supanext.com" target="_blank"><img alt="Firebase" srcSet="/_next/image?url=%2Fblog%2Fsupabase.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2Fblog%2Fsupabase.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2Fblog%2Fsupabase.png&amp;w=1920&amp;q=75" width="700" height="400" decoding="async" data-nimg="1" class="rounded-lg shadow-md transition-shadow duration-300 hover:shadow-lg" loading="lazy" style="color:transparent"/></a>
<h2>My Journey from Firebase to Supabase</h2>
<p>Having worked with Firebase for a while, I appreciated its ease of use and the ability to quickly create new applications. However, I&#x27;ve found myself gravitating towards Supabase and there really is no way to go back.</p>
<p>The transition to a SQL-based database allowed me to leverage the full power of relational data modeling. The familiarity of SQL made querying data more straightforward and efficient for my use cases. The developer tools, ease of use, and the UI of Supabase resonated with me, making development a more enjoyable experience.</p>
<p>I was so impressed with Supabase that I decided to create my own boilerplate for Next.js projects using Supabase. You can learn more at <a href="https://www.supanext.com/">Supanext.com</a>.</p>
<a href="https://www.supanext.com" target="_blank"><img alt="SupaNext Boilerplate" srcSet="/_next/image?url=%2Fblog%2Fsupanext.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2Fblog%2Fsupanext.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2Fblog%2Fsupanext.png&amp;w=1920&amp;q=75" width="700" height="400" decoding="async" data-nimg="1" class="rounded-lg shadow-md transition-shadow duration-300 hover:shadow-lg" loading="lazy" style="color:transparent"/></a>
<h2>How Are They Similar?</h2>
<p>Both Firebase and Supabase aim to simplify backend development by providing ready-to-use services, allowing developers to focus more on building the frontend. Some of the similarities include:</p>
<ul>
<li><strong>Rapid Project Setup</strong>: You can spin up new projects directly from the browser without needing additional tools.</li>
<li><strong>Real-time Data</strong>: Both support real-time data synchronization between the client and server.</li>
<li><strong>Authentication and Storage</strong>: Easy implementation of user authentication and file storage solutions.</li>
<li><strong>Client Libraries</strong>: SDKs for interacting with their services directly from your app.</li>
<li><strong>Dashboard UI</strong>: Intuitive dashboards for managing and debugging your data in real-time.</li>
</ul>
<p>In my experience, both platforms offer a smooth developer experience, but the choice between them often comes down to the specific needs of your project. For mobile app developers, <a href="https://calljmp.com/">Calljmp</a> also shares many of these same characteristics, offering a complete backend solution with realtime capabilities, authentication, and storage - all built on Cloudflare&#x27;s edge infrastructure.</p>
<h2>How Are They Different?</h2>
<h3>Database Structure</h3>
<ul>
<li><strong>Firebase</strong>: Uses a <strong>NoSQL document-based</strong> database (Firestore), which is great for unstructured data and allows flexible data models.</li>
<li><strong>Supabase</strong>: Uses a <strong>relational SQL database</strong> (PostgreSQL), ideal for structured data with complex relationships and advanced querying.</li>
</ul>
<p>For me, working with a relational database like PostgreSQL made data modeling more intuitive, especially for applications requiring complex queries.</p>
<h3>Open Source</h3>
<ul>
<li><strong>Supabase</strong>: Completely open-source. You can host it yourself, modify it, and there&#x27;s no vendor lock-in.</li>
<li><strong>Firebase</strong>: Proprietary and owned by Google. While robust, it doesn&#x27;t offer the same level of transparency or self-hosting capabilities.</li>
</ul>
<p>The open-source nature of Supabase aligns with my preference for tools that offer more control and flexibility.</p>
<h3>Supabase vs Firebase Pricing</h3>
<ul>
<li><strong>Firebase</strong>: Charges based on the number of reads, writes, and deletes, which can lead to unpredictable costs as your app scales.</li>
<li><strong>Supabase</strong>: Charges based on the amount of data stored, with unlimited API requests and authentication users, offering more predictable pricing.</li>
</ul>
<p>Predictable costs are essential, especially when scaling a project. Supabase&#x27;s pricing model gives me peace of mind knowing that I won&#x27;t be hit with unexpected charges.</p>
<h3>Supabase vs Firebase Performance</h3>
<ul>
<li><strong>Firebase</strong>: Excels in real-time synchronization and rapid development but may become complex with large datasets and intricate querying needs.</li>
<li><strong>Supabase</strong>: Leverages PostgreSQL for high performance, especially with complex queries and transactional workloads.</li>
</ul>
<p>In my projects, I&#x27;ve noticed that Supabase handles complex data operations more efficiently, which has been a significant advantage.</p>
<h2>Why I Prefer Supabase Now</h2>
<p>After a decade in development and transitioning to working full-time on my own projects, I&#x27;ve found that Supabase aligns better with my current needs:</p>
<ul>
<li><strong>Developer Tools</strong>: Supabase&#x27;s tools are intuitive and powerful, making development a joy.</li>
<li><strong>Ease of Use</strong>: Setting up and managing databases feels straightforward and hassle-free.</li>
<li><strong>Documentation</strong>: Comprehensive docs make it easy to find solutions and best practices.</li>
<li><strong>UI/UX</strong>: The dashboard is clean and user-friendly, enhancing the overall experience.</li>
<li><strong>Relational Database</strong>: Being able to use SQL and model complex relationships is a big plus for me.</li>
</ul>
<blockquote>
<p>My positive experience with Supabase inspired me to create <a href="https://www.supanext.com/">Supanext</a>, a boilerplate that combines Next.js with Supabase. It comes packed with AI examples and aims to help developers kickstart their projects even faster.</p>
</blockquote>
<a href="https://www.supanext.com" target="_blank"><img alt="SupaNext Boilerplate" srcSet="/_next/image?url=%2Fblog%2Fsupanext-2.png&amp;w=750&amp;q=75 1x, /_next/image?url=%2Fblog%2Fsupanext-2.png&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2Fblog%2Fsupanext-2.png&amp;w=1920&amp;q=75" width="700" height="400" decoding="async" data-nimg="1" class="rounded-lg shadow-md transition-shadow duration-300 hover:shadow-lg" loading="lazy" style="color:transparent"/></a>
<h2>When to Choose Firebase</h2>
<p>That said, Firebase is still a fantastic option for certain scenarios:</p>
<ul>
<li><strong>Rapid Prototyping</strong>: If you need to get a simple app up and running quickly.</li>
<li><strong>Unstructured Data</strong>: When your data doesn&#x27;t fit a strict schema, and you prefer flexibility.</li>
<li><strong>Google Ecosystem</strong>: If you&#x27;re already using Google Cloud services and want seamless integration.</li>
<li><strong>Comprehensive Toolset</strong>: Firebase offers additional services like machine learning, analytics, and cloud messaging.</li>
</ul>
<h2>When to Choose Supabase</h2>
<p>Consider Supabase if:</p>
<ul>
<li><strong>Structured Data Needs</strong>: Your app requires complex queries, relationships, and transactional integrity.</li>
<li><strong>Preference for SQL</strong>: You want to leverage the power and familiarity of SQL.</li>
<li><strong>Open Source</strong>: You value transparency and the ability to self-host, avoiding vendor lock-in.</li>
<li><strong>Predictable Pricing</strong>: You prefer costs based on storage rather than operations, allowing for better budgeting.</li>
</ul>
<h2>Final Thoughts</h2>
<p>Both Firebase and Supabase have their strengths, and the best choice depends on your project&#x27;s requirements. For me, Supabase has become the go-to backend service, offering the flexibility and power I need for my projects.</p>
<p>If you&#x27;re undecided, I recommend trying both platforms to see which one resonates with you. And if you have any questions or want to share your experiences, feel free to reach out on <a href="https://x.com/jake_prins">X</a>.</p>
<hr/>
<p><em>Thanks for reading! If you&#x27;d like to stay updated on my projects, you can follow me on <a href="https://twitter.com/jake_prins">Twitter</a> or check out <a href="https://www.supanext.com/">Supanext.com</a>.</em></p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Simplifying Web3 for Mass Adoption]]></title>
            <link>https://jakeprins.com/blog/simplifying-web3-for-mass-adoption</link>
            <guid>https://jakeprins.com/blog/simplifying-web3-for-mass-adoption</guid>
            <pubDate>Tue, 09 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The two essential focus points for more widespread adoption of blockchain applications.]]></description>
            <content:encoded><![CDATA[<p>Blockchain apps are different from web2 apps in many ways. Decentralized networks are not owned or managed by a single authority and and very useful to keep track of who owns what. This adds a new dimension to the web: <strong>digital ownership</strong>.</p>
<p>Digital ownership is the crucial concept that makes NFTs and blockchain-based accounts so valuable. It allows digital items and identities to be shared across multiple platforms and gives the users more control. However, people who are new to the world of Web3 face many challenges when they try to get started. This stops the adoption of this new technology. <strong>Even though many people own crypto through exchanges, only a tiny number of people are active blockchain users and make regular transactions with their own custodial wallets.</strong></p>
<p>If we want to use crypto for more than just buying and selling, we must improve the technology’s basic building blocks so they can be used for creating complex and exciting consumer applications. We can make changes, like pushing the adoption of smart contract-based accounts (like <a href="https://universal.page/universal-profiles">Universal Profiles</a>) instead of key-based wallets (like <a href="https://metamask.io/">MetaMask</a>). Still, before we do that, we need to focus on two essential principles necessary for the more widespread adoption of Web3.</p>
<h2>User Experience</h2>
<p>It might speak for itself at this point, but the first and most important key to growing blockchain adoption is user experience. Unfortunately, for new users, the process of making transactions is still <a href="https://medium.com/@jakeprins/the-trouble-with-starting-in-web3-and-how-to-solve-it-186a8990ed2c">quite complicated and intimidating</a>. To make a simple transaction, users have to go through many steps. They need to buy tokens to cover the transaction fees (gas), go through KYC, send the tokens to their wallet, and hope everything goes well.</p>
<p>Creating a wallet is also a requirement, which needs to be done through a browser extension or mobile app. The risk of losing a private key can also be a nightmare. Even after completing all the technical requirements, the wallet itself is pretty basic and lacks features and extensibility. It’s not much more than a public and private key, but somehow, everything in Web3 is built around this, even the non-financial apps. The use of key-based wallets is indirectly hindering mainstream adoption.</p>
<p>To make blockchain technology more accessible to the masses, we need to simplify the process and make it more user-friendly. For real adoption to happen, users should be able to create their profile (profiles &gt; wallets) that serves as their Web3 identity. This profile should allow them to move from dapp to dapp and serve as their wallet where they can hold their tokens and NFTs. They should also be able to make a transaction, such as minting a free NFT, in under a minute, preferably with <a href="https://universal.page/blog/make-digital-ownership-accessible-to-everyone">user-friendly options like using their email</a> or mobile phone to sign up and get started. Only then can we start thinking about onboarding the masses. <strong>It’s not a nice-to-have; it’s a requirement.</strong></p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*-CxEepbuW8uF6OmEYMbRgA.png" alt="Use email to create a onchain profile on universal.page"/>
<em>Use email to create a onchain profile on <a href="https://universal.page/">universal.page</a></em></p>
<h2>Education</h2>
<p>To achieve the first principle of web3 onboarding, we must move the technology to the background to make the user experience more comparable with web2 apps. However, people still need to learn about digital ownership and the new concepts that blockchain apps enable.</p>
<p>Blockchains are backend technology, and just like web2, they could be completely hidden from the user. This may make sense, as users also don’t care whether <a href="https://netflix.com/">Netflix</a> or <a href="https://discord.com/">Discord</a> is built on top of <a href="https://aws.amazon.com/">AWS</a>, <a href="https://cloud.google.com/">Google Cloud</a>, or its own servers. However, this is different. While it’s important for a great user experience, users still need to learn about the new concepts of digital ownership. When someone buys a digital collectible, it is essential to know the blockchain where it is stored. It’s also important to know if a blockchain is even involved in the first place. The ownership of a digital item depends on whether it is stored in an open, decentralized system or a closed, centralized one. If the latter is the case, users may be unable to use the item outside the system.</p>
<p>A friend of mine once bought a digital collectible that he assumed was an NFT, but it turned out it was not. Although the label “digital collectible” might sound more user-friendly, and many people in the NFT space like to use this word more often, you still want to know if it’s an NFT. You need to know if it’s stored on a public blockchain because that’s what enables true digital ownership.</p>
<p>The same goes for your user account. Are you creating an account with a centralized platform or a blockchain account or wallet that you can fully control and take with you? Are you renting your account, and can it be taken from you at any time, or can you use an <a href="https://universal.page/universal-profiles">onchain profile</a> that you fully control and can take with you to other platforms?</p>
<p>New users shouldn’t get overwhelmed with technical terms and new concepts. <strong>Still, after a seamless onboarding experience, education is just as important.</strong> There is a difference between storing something on a centralized service and on a blockchain. It defines digital ownership, a new dimension on the web that many have yet to experience and learn about.</p>
<h2>Conclusion</h2>
<p>For blockchain applications to be widely adopted, the user experience must improve, and new users must be educated about new concepts in a seamless way. To achieve the full potential of Web3, we need to simplify the onboarding process and make it as seamless as any Web2 application. Additionally, we need to explain blockchain technology in a way everyone can understand, emphasizing the real-life benefits of digital ownership. By focusing on these key areas, we can create a decentralized web that is inclusive and accessible to all. As builders of blockchain applications, it’s our shared responsibility to guide, educate, and empower users in this transition to a more open, equitable, and interconnected online world.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[The Trouble with Starting in Web3 and How to Solve It.]]></title>
            <link>https://jakeprins.com/blog/the-trouble-with-starting-in-web3-and-how-to-solve-it</link>
            <guid>https://jakeprins.com/blog/the-trouble-with-starting-in-web3-and-how-to-solve-it</guid>
            <pubDate>Tue, 02 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The onboarding experience for new people using blockchain applications is terrible. ]]></description>
            <content:encoded><![CDATA[<p>The onboarding experience for new people using blockchain applications is terrible. Imagine recommending a friend mint a free NFT at some platform, and think of all the hoops this person needs to jump through to get started.</p>
<p>First, you must download a browser extension to create a digital wallet. Then, you must write down random words to keep your wallet safe.</p>
<p>When you try to mint a free NFT, you realize you need to pay something called “gas” to make a transaction. You ask a friend for help, and they suggest that you buy some cryptocurrency first. So, you sign up for a crypto exchange.</p>
<p>Now, the exchange wants you to complete KYC (know your customer) verification, which can be lengthy. After waiting some time, you finally purchase some tokens to get that free NFT.</p>
<p>However, you find out that there is a minimum withdrawal amount of $50, so you go back and buy more tokens. When you finally have enough funds, you transfer them to your wallet, but nothing happens. You become anxious and worried that you made a mistake. After waiting for a while, the transfer finally goes through. You end up with $40 because there was a fixed $10 fee from the exchange to withdraw.</p>
<p>Now, you’re ready to mint that NFT, but it took you too long, and the mint sold out. You can still buy it on the secondary market for around $40. Although disappointed, you try it, but it doesn’t work because of high gas fees, and you still need more crypto in your wallet.</p>
<p>Now, what happens next? You could continue minting some other random NFT (which could be a scam), hop into a Discord server of a random project, and say “GM” (though nobody is saying it back), or buy more tokens and try again (and might get rugged).</p>
<p>But you will likely quit. You might think that you need help understanding Web3 or that it’s all just a scam. And who can blame you? You need to be highly motivated to complete the onboarding experience, and it can be very disappointing if you are not rewarded.</p>
<h2><strong>What Web3 onboarding should be like</strong></h2>
<p>To make Web3 more accessible, a few fundamental problems need to be addressed. Firstly, the high gas fees associated with owning a free NFT discourage many users from making their first transaction on a blockchain. To buy crypto and sign up at an exchange is an additional hurdle that most people are unwilling to overcome.</p>
<p>One solution could be to use blockchain accounts instead of key-based wallets. With blockchain accounts like <a href="https://universal.page/universal-profiles">Universal Profiles</a>, a transaction relayer service can enable someone else to pay for the user’s gas fees. A service provider, like, for example, an NFT marketplace, can cover the initial few transactions for new users. This initiative can bring more people into the platform and foster many active traders. The marketplace can then earn a small fee on each trade to recover the costs of subsidizing gas for new users. This approach offers everyone a seamless first experience and brings more people into the space.</p>
<p>Before even using NFTs, DeFi, or Web3 Social, users often face the hurdle of installing a browser extension or mobile wallet. For those new to this technology, installing an extension, remembering random words, and risking losing their digital assets can be challenging. All of this can be overwhelming.</p>
<p>Some Web3 platforms choose to create wallets for their users and manage them centrally, similar to a crypto exchange. This way some NFT platforms could offer a “gassless” experience and onboarding through email, but the actual implementation of how this works is quite crucial. Most platforms use an approach that has a significant drawback — <strong>it takes away one of the most important aspects of Web3, which is digital ownership</strong>. The saying “not your keys, not your crypto” holds true here. If the platform has complete control over your wallet and you cannot take control of it, it defeats the purpose of digital ownership.</p>
<p>On one end, we want to make blockchain technology seamless for users. However, there should be a balance. Educating users about digital ownership is a crucial aspect of Web3 onboarding. People should learn about the value of digital ownership and be able to take complete control over their digital assets whenever they are ready to do so.</p>
<p>The NFT platform <a href="https://universal.page/"><strong>universal.page</strong></a> uses <a href="https://docs.lukso.tech/standards/universal-profile/introduction/">Universal Profiles</a> instead of key-based wallets. These profiles allow you to set permissions on your account to perform specific actions. To sign up, you must provide your email address, create a Universal Profile, and secure it with a password. The platform has some permissions on the profile but cannot take over control and is not able to perform any transactions without your password. It’s easy to sign in and confirm transactions with a password, such as buying and selling NFTs, but it’s even better that you do so without having to pay for gas. You can export your profile to the <a href="https://chromewebstore.google.com/detail/universal-profiles/abpickdkkbnbcoepogfhkhennhfhehfn">Universal Profiles browser extension</a> or mobile app anytime and revoke any permissions to regain complete control.</p>
<p>From the very beginning, you are the sole owner of your profile. You can easily take it when you are ready to use your profile on other dApps. You control the permissions and own the assets, ensuring that digital ownership is introduced securely and transparently.</p>
<h2><strong>Conclusion</strong></h2>
<p><strong>The Web3 experience is not broken; it’s still in the making.</strong> Everyone in this space knows how poor the onboarding process is, and it is time to improve it. We now have the technology to do so. With account abstraction, such as <a href="https://universal.page/universal-profiles">Universal Profiles</a>, we can provide users with the Web2 experience without compromising Web3 principles. The key to mass adoption is to present digital ownership through education and a seamless user experience.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Make Digital Ownership Accessible to Everyone.]]></title>
            <link>https://jakeprins.com/blog/make-digital-ownership-accessible-to-everyone</link>
            <guid>https://jakeprins.com/blog/make-digital-ownership-accessible-to-everyone</guid>
            <pubDate>Fri, 05 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Why education and user experiences are the keys to the digital ownership revolution.]]></description>
            <content:encoded><![CDATA[<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*evvcxIJSj8pmXCXoXuHcKQ.png" alt="Make Digital Ownership Accessible to Everyone."/></p>
<p><a href="/blog/why-digital-ownership-matters">Digital ownership is important</a>. For many years, it was only possible with the help of trusted centralized intermediaries. Decentralized blockchains have changed this, and digital ownership is now possible without the need of a single third-party keeping track of who owns what. However, just like the early days of the internet, this new paradigm is only accessible to tech enthusiasts, early adopters, and nerds. It seems like only they care about it. But times are changing, and it is becoming more apparent that digital ownership will become the norm, used in various products and services. <strong>From fashion to gaming, finance to music, and art to entertainment, many industries can use digital ownership to innovate.</strong></p>
<p>However, it’s not inevitable that this will happen. Making digital ownership accessible to everyone is not a simple task. There are challenges to overcome, like the complexity of concepts like wallets and private keys, which are difficult for everyday users to understand and too limiting for developers to create something that can truly unlock its potential.</p>
<p>This is made worse by the fact that there is a multitude of tokens available for purchase and speculation, which further complicates things. The ecosystem currently consists of a relatively small group of active users, with most people in the crypto space only here to speculate. However, there is a missed opportunity here. <strong>Underneath the hype, speculation, and distraction, there is a technology that can bring ownership to the internet and make it accessible to everyone.</strong></p>
<p>To make digital ownership accessible, there are a few things that need to happen. Firstly, people must learn about new concepts. <strong>Although technology should be abstracted to the background, people still need to understand how digital ownership works.</strong> For example, someone could buy a digital collectible, but it doesn’t necessarily mean that it’s stored on a blockchain and its ownership can be verified. Even though the term <em>NFT</em> has become a negative connotation for some people, they still need to know whether a collectible is an NFT or not. Education plays a big role.</p>
<p><strong>For everyday users to embrace new concepts, they first need to have a great user experience.</strong> The current steps to get started are too complicated and overwhelming. Downloading a browser extension, creating a wallet, saving seed phrases, buying tokens for “gas” — all these steps are daunting. To introduce someone to a new concept, they should be able to sign up and get an NFT without leaving the site. They may not understand what it means to own an NFT, but they can learn about it later. The first step is to get through the process seamlessly.</p>
<p>Once someone has an NFT, they can learn why it’s essential to store it on a blockchain. <strong>It’s better to show them than to tell them.</strong> For example, on <a href="https://universal.page/">universal.page</a>, users can sign up and mint an NFT that serves as their domain for a customizable profile page. They can experience the utility of the NFT because they can start building their page directly without worrying about concepts like “gas fees”. Later, they can learn that this domain can be sold on the open marketplace, and the new owner will get control over the domain and page, all permissionless. This will help them understand that they <em>own</em> the NFT and can transfer or sell it to someone else. We need more experiences like this that are simple, seamless and focused on utility.</p>
<h2>Summary</h2>
<p><strong>The key to making digital ownership accessible for everyone is to simplify the user experience and educate people along the way.</strong> By making the technology less intimidating and highlighting its practical benefits, we can turn digital ownership into a tool for innovation and empowerment available to everyone.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Why Digital Ownership Matters.]]></title>
            <link>https://jakeprins.com/blog/why-digital-ownership-matters</link>
            <guid>https://jakeprins.com/blog/why-digital-ownership-matters</guid>
            <pubDate>Thu, 04 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[How Blockchains are Transforming Ownership, Digital Identity, and Empowering Users.]]></description>
            <content:encoded><![CDATA[<h2>How Blockchains are Transforming Ownership, Digital Identity, and Empowering Users</h2>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*hF_yVpiaz4-y929VHGzoWg.png" alt="Why Digital Ownership Matters."/></p>
<p>The internet has been the most profound and impactful invention in human history, and we have experienced how it changed the world forever. What started as something only interesting for nerds, who created funny-looking websites only visited by other nerds, grew into something the whole world depended upon in their day-to-day lives. It started a wave of innovation that changed the way we live forever.</p>
<p>The internet has connected people across the globe, allowing them to communicate, play games, work remotely, make friends, and have digital experiences.</p>
<p>Those digital experiences have value, just like the digital things we create and buy. But many digital things we buy lack aspects we experience in the physical world.</p>
<p>When we purchase tickets online, they are delivered to us as “E-tickets” via email. However, unlike physical tickets, we do not technically own these E-tickets. This means we cannot give our ticket to someone else if we cannot attend the event.</p>
<p>This raises the question of why we do not own the tickets we have paid for. For example, if we buy a plane ticket, we would expect it to give us the right to a specific seat on the plane. However, we do not technically own that seat and, therefore, cannot sell or transfer it to someone else if we cannot use it. Interestingly, airlines tend to overbook flights since some ticket holders do not show up for their flights.</p>
<p><strong>As digital items continue to hold more value, ownership becomes increasingly essential.</strong> As a gamer, I’ve spent countless hours attempting to unlock in-game items, from NBA 2K jerseys to Call of Duty rifles to Apex Legends weapon skins. However, as I grew older and lacked time to grind, I began to pay for these items. Unfortunately, in the case of NBA 2K, these payments added up quickly, and each year, upon the release of the newest game, I found myself starting from scratch.</p>
<p>Digital items like game items, tickets, or digital art have a significant problem — the need for actual ownership. In most cases, the ownership of these items is tracked in centralized databases controlled by providers, who decide what you can and can’t do with them. They pretend to be yours, but in reality, they are not.</p>
<p>Most of these items exist in centralized databases under the complete control of the companies that provide them, creating another issue for digital ownership. Additionally, digital ownership has been complex since the birth of the internet because transferring digital files to another computer can only be done by making a copy.</p>
<p>If I give you a painting in the physical world, it means I no longer have it. However, if I send you a digital artwork over the Internet, we both have it. This makes tracking ownership much harder, and only possible by leveraging a trusted third party to help us keep track of ownership.</p>
<p>Thanks to the emergence of blockchain technology, we can now have a trusted third party that is decentralized and entirely operated by a network of computers. This technology adds a new dimension to the internet, allowing people to own digital items.</p>
<p>It&#x27;s not just about digital items but also <strong>digital identity</strong>. Your social media accounts, such as Twitter, Instagram, or TikTok, are not really owned by you. You are essentially renting them, and the companies controlling them can take them away at any moment. Keeping your followers, activity, and creations in a closed system is beneficial to these companies because it creates a &quot;vendor lock-in,&quot; making it less likely for you to move to a different platform. With blockchain-based accounts such as <a href="https://docs.lukso.tech/standards/universal-profile/introduction/"><strong>Universal Profiles</strong></a>, the power goes back to the user, who has full control over their profile and can take their belongings with them wherever they go. This forces platforms to stay innovative and keep improving the user experience because it&#x27;s much easier for users to switch to a competitor.</p>
<p><strong>Now that digital ownership is possible, it is up to creators, developers, and companies to build a new area of the internet.</strong> Even though the technology is available, this new world will not be built by itself. It requires creatives to leverage this technology to showcase how digital ownership can change things. It needs the nerds and early enthusiasts to build the tools for others to build upon. Similar to the early days of the internet, we need people who understand that digital ownership matters and are willing to work hard to make it accessible to everyone.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Convert a Next.js Application to React.js]]></title>
            <link>https://jakeprins.com/blog/how-to-convert-nextjs-to-reactjs</link>
            <guid>https://jakeprins.com/blog/how-to-convert-nextjs-to-reactjs</guid>
            <pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[A step-by-step guide on converting your Next.js application to a pure React.js application.]]></description>
            <content:encoded><![CDATA[<h1>How to Convert a Next.js Application to React.js</h1>
<p><em>Last updated: October 1, 2023</em></p>
<p>If you&#x27;ve built a web application using Next.js but now need to convert it to a pure React.js application, you&#x27;re not alone. Whether it&#x27;s for simplicity, specific project requirements, or a preference for client-side rendering, converting from Next.js to React can be straightforward with the right guidance. In this article, I&#x27;ll walk you through the steps to <strong>convert a Next.js app to React.js</strong>, ensuring a smooth transition while maintaining your application&#x27;s core functionality.</p>
<blockquote>
<p><strong>Note:</strong> If you&#x27;re looking to convert a React.js application to Next.js instead, check out my <a href="https://www.jakeprins.com/blog/how-to-convert-reactjs-to-nextjs">other article on how to convert React.js to Next.js</a>.</p>
</blockquote>
<h2>Why Convert Next.js to React.js?</h2>
<p>Next.js is a fantastic framework that offers server-side rendering (SSR), static site generation (SSG), and a host of other features out of the box. However, these features might be overkill for smaller projects or unnecessary if you prefer client-side rendering (CSR) with React.js. Converting to React.js can simplify your build process, reduce server dependencies, and give you more control over your application&#x27;s architecture.</p>
<h2>Understanding the Differences</h2>
<p>Before diving into the conversion process, it&#x27;s essential to understand the key differences between Next.js and React.js:</p>
<ul>
<li><strong>Server-Side Rendering vs. Client-Side Rendering:</strong> Next.js supports SSR and SSG, whereas React.js is primarily for CSR.</li>
<li><strong>Routing:</strong> Next.js has a built-in file-based routing system. In React.js, you&#x27;ll need to use a library like React Router.</li>
<li><strong>API Routes:</strong> Next.js allows you to create API routes within the same project. In React.js, you&#x27;d typically separate the frontend and backend.</li>
</ul>
<h2>Steps to Convert Next.js to React.js</h2>
<p>Let&#x27;s get started with the step-by-step guide on how to convert a Next.js application to React.js.</p>
<h3>1. Set Up a New React.js Application</h3>
<p>First, create a new React.js project using a tool like Create React App (CRA) or Vite.</p>
<p>Using Create React App:</p>
<pre class="language-bash"><code class="language-bash">npx create-react-app my-react-app
<span class="token builtin class-name">cd</span> my-react-app
</code></pre>
<p>Using Vite:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> init vite@latest my-react-app --template react
<span class="token builtin class-name">cd</span> my-react-app
<span class="token function">npm</span> <span class="token function">install</span>
</code></pre>
<h3>2. Transfer Dependencies</h3>
<p>Copy the dependencies and devDependencies from your Next.js project&#x27;s package.json to the new React.js project&#x27;s package.json. Run <code>npm install</code> or <code>yarn install</code> to install them.</p>
<h3>3. Migrate Components and Pages</h3>
<p>Copy the contents of your Next.js components and pages directories into your React.js project&#x27;s src directory. Since Next.js uses a pages directory for routing, you&#x27;ll need to adjust this structure.</p>
<h3>4. Implement Routing with React Router</h3>
<p>Next.js handles routing automatically based on the pages directory. In React.js, you&#x27;ll need to set up routing manually.</p>
<p>Install React Router:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> react-router-dom
</code></pre>
<p>Set Up Routes in src/App.js:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">BrowserRouter</span> <span class="token keyword module">as</span> <span class="token maybe-class-name">Router</span><span class="token punctuation">,</span> <span class="token maybe-class-name">Routes</span><span class="token punctuation">,</span> <span class="token maybe-class-name">Route</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-router-dom&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Home</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;./Home&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">About</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;./About&#x27;</span>
<span class="token comment">// Import other components as needed</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">App</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Router</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Routes</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/<span class="token punctuation">&quot;</span></span> <span class="token attr-name">element</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Home</span></span> <span class="token punctuation">/&gt;</span></span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/about<span class="token punctuation">&quot;</span></span> <span class="token attr-name">element</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">About</span></span> <span class="token punctuation">/&gt;</span></span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
        </span><span class="token punctuation">{</span><span class="token comment">/* Add other routes */</span><span class="token punctuation">}</span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Routes</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Router</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">App</span>
</code></pre>
<h3>5. Adjust Data Fetching Methods</h3>
<p>In Next.js, data fetching is done using getServerSideProps, getStaticProps, or getInitialProps. In React.js, you&#x27;ll need to handle data fetching within your components, typically using useEffect and useState.</p>
<p>Example:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useState<span class="token punctuation">,</span> useEffect <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Home</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>data<span class="token punctuation">,</span> setData<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

  <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">&#x27;/api/data&#x27;</span><span class="token punctuation">)</span> <span class="token comment">// Update the API endpoint as needed</span>
      <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> response<span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">setData</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span><span class="token comment">/* Render your data */</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Home</span>
</code></pre>
<h3>6. Handle API Routes</h3>
<p>If your Next.js app uses API routes within the pages/api directory, you&#x27;ll need to migrate these to a separate backend service or adjust your frontend to fetch from external APIs.</p>
<p>Options:</p>
<ul>
<li>Create a Separate Backend: Use Node.js, Express, or any backend framework to set up API endpoints.</li>
<li>Use External APIs: If possible, adjust your frontend to consume external APIs directly.</li>
</ul>
<h3>7. Update Environment Variables</h3>
<p>Next.js uses environment variables prefixed with <code>NEXT_PUBLIC_</code> for client-side variables. In React.js, create a .env file at the root of your project and prefix variables with <code>REACT_APP_</code>.</p>
<p>Example:</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">REACT_APP_API_URL</span><span class="token operator">=</span>https://api.example.com
</code></pre>
<p>Access them in your code using <code>process.env.REACT_APP_API_URL</code>.</p>
<h3>8. Remove Next.js Specific Configurations</h3>
<p>Delete any Next.js specific configurations like next.config.js. Also, remove any Next.js-specific imports or packages that are no longer needed.</p>
<h3>9. Test Your Application</h3>
<p>Run your React.js application to ensure everything works as expected.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> start
</code></pre>
<p>Go through each route and functionality to verify that the conversion was successful.</p>
<h2>Conclusion</h2>
<p>Converting a Next.js application to React.js involves setting up a new React project and migrating your components, adjusting routing, and handling data fetching differently. While Next.js offers many powerful features, a pure React.js application provides simplicity and flexibility, especially for projects that don&#x27;t require server-side rendering.</p>
<p>By following this guide on how to convert Next.js to React.js, you should have a seamless transition.</p>
<p>Thanks for reading! If you&#x27;re interested in going the other way around, check out my article on <a href="https://www.jakeprins.com/blog/how-to-convert-reactjs-to-nextjs">how to convert a React.js application to Next.js</a>. For more updates on new projects or articles, follow me on Twitter: <a href="https://twitter.com/jake_prins">@jake_prins</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Convert a React.js Application to Next.js]]></title>
            <link>https://jakeprins.com/blog/how-to-convert-reactjs-to-nextjs</link>
            <guid>https://jakeprins.com/blog/how-to-convert-reactjs-to-nextjs</guid>
            <pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[A comprehensive guide on converting your React.js application to Next.js.]]></description>
            <content:encoded><![CDATA[<h1>How to Migrate from React.js to Next.js</h1>
<p><em>Last updated: October 1, 2023</em></p>
<p>If you&#x27;re considering enhancing your React.js application with server-side rendering, improved SEO, and a better developer experience, migrating to Next.js is a great choice. In this article, I&#x27;ll guide you through the process of <strong>migrating a React.js app to Next.js</strong>, ensuring a smooth transition while leveraging the powerful features that Next.js offers.</p>
<blockquote>
<p><strong>Note:</strong> If you&#x27;re looking to convert a Next.js application to React.js instead, check out my <a href="https://www.jakeprins.com/blog/how-to-convert-nextjs-to-reactjs">other article on how to convert Next.js to React.js</a>.</p>
</blockquote>
<h2>Why Migrate to Next.js?</h2>
<p>Next.js is a React framework that provides out-of-the-box support for server-side rendering (SSR), static site generation (SSG), and easy routing, among other features. Migrating to Next.js can offer:</p>
<ul>
<li><strong>Improved Performance:</strong> Through SSR and SSG, your app can deliver content faster.</li>
<li><strong>Better SEO:</strong> Server-rendered pages are more SEO-friendly.</li>
<li><strong>Simplified Routing:</strong> File-based routing system simplifies navigation setup.</li>
<li><strong>Built-in CSS Support:</strong> Easily manage styles with CSS Modules and styled-jsx.</li>
</ul>
<h2>Steps to Migrate React.js to Next.js</h2>
<p>Let&#x27;s dive into the step-by-step guide on <strong>how to migrate a React.js application to Next.js</strong>.</p>
<h3>1. Set Up a New Next.js Application</h3>
<p>First, create a new Next.js project. You can use the Next.js CLI to set up the project quickly:</p>
<pre class="language-bash"><code class="language-bash">npx create-next-app@latest my-next-app
<span class="token builtin class-name">cd</span> my-next-app
</code></pre>
<h3>2. Copy Public Assets</h3>
<p>If your React app has a public directory containing assets like images or fonts, copy all the contents into the public directory of your Next.js app.</p>
<h3>3. Install Necessary Dependencies</h3>
<p>Copy the dependencies and devDependencies from your React app&#x27;s package.json to the Next.js app&#x27;s package.json. Then run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span>
</code></pre>
<h3>4. Migrate Components</h3>
<p>Copy your src/components directory from your React app to the Next.js app. Since Next.js uses the pages directory for routing, we&#x27;ll handle pages separately.</p>
<h3>5. Migrate Pages and Adjust Routing</h3>
<p>In your React app, you might have a src directory with pages managed by React Router. In Next.js, you&#x27;ll use the pages directory for routing.</p>
<p>Create Pages: Move your page components to the pages directory. For example, your Home.js component would become pages/index.js.</p>
<p>Example:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token comment">// pages/index.js</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Header</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;../components/Header&#x27;</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">HomePage</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Header</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Welcome to My Next.js App</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">HomePage</span>
</code></pre>
<h3>6. Update Routing Logic</h3>
<p>Since Next.js uses file-based routing, you don&#x27;t need React Router. Remove React Router dependencies:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> uninstall react-router-dom
</code></pre>
<p>Adjust any routing logic in your components to use Next.js&#x27;s Link component.</p>
<p>Example:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Link</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next/link&#x27;</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Navigation</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/about<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">About</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/contact<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Contact</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Navigation</span>
</code></pre>
<h3>7. Handle Static Assets and CSS</h3>
<p>CSS Files: Next.js supports global CSS files in pages/_app.js.</p>
<p>Example:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token comment">// pages/_app.js</span>
<span class="token keyword module">import</span> <span class="token string">&#x27;../styles/global.css&#x27;</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">MyApp</span></span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> <span class="token maybe-class-name">Component</span><span class="token punctuation">,</span> pageProps <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Component</span></span> <span class="token spread"><span class="token punctuation">{</span><span class="token spread operator">...</span>props<span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">MyApp</span>
</code></pre>
<p>CSS Modules: For component-level styling, you can use CSS Modules.</p>
<p>Example:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token comment">// components/Button.js</span>
<span class="token keyword module">import</span> <span class="token imports">styles</span> <span class="token keyword module">from</span> <span class="token string">&#x27;./Button.module.css&#x27;</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Button</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>styles<span class="token punctuation">.</span><span class="token property-access">primary</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Click Me</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Button</span>
</code></pre>
<h3>8. Adjust Data Fetching Methods</h3>
<p>Replace any data fetching in useEffect that should occur at build time or on the server with Next.js&#x27;s data fetching methods.</p>
<ul>
<li><code>getStaticProps</code>: For static generation.</li>
<li><code>getServerSideProps</code>: For server-side rendering.</li>
</ul>
<p>Example using getStaticProps:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token comment">// pages/index.js</span>
<span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">&#x27;https://api.example.com/data&#x27;</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword control-flow">await</span> res<span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      data<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">HomePage</span></span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> data <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token comment">// Render your component using the fetched data</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">HomePage</span>
</code></pre>
<h3>9. Update Environment Variables</h3>
<p>Next.js uses environment variables prefixed with NEXT<em>PUBLIC</em> for client-side variables.</p>
<p>Example:</p>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">NEXT_PUBLIC_API_URL</span><span class="token operator">=</span>https://api.example.com
</code></pre>
<p>Access them in your code using <code>process.env.NEXT_PUBLIC_API_URL</code>.</p>
<h3>10. Remove Unnecessary Packages</h3>
<p>Since Next.js provides many features out of the box, you can remove packages that are no longer needed, such as:</p>
<ul>
<li>react-scripts</li>
<li>react-router-dom</li>
<li>Any custom SSR setup</li>
</ul>
<h3>11. Test Your Application</h3>
<p>Run your Next.js application to ensure everything works as expected:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> run dev
</code></pre>
<p>Navigate through your app to verify that pages load correctly, data is fetched properly, and styles are applied.</p>
<h2>Conclusion</h2>
<p>Migrating from React.js to Next.js can significantly enhance your application&#x27;s performance and SEO capabilities. While the process involves several steps, Next.js&#x27;s powerful features make it worth the effort.</p>
<p>By following this guide on how to migrate from React.js to Next.js, you should be able to transition smoothly. If you have any questions or encounter issues, feel free to reach out or leave a comment below.</p>
<p><strong>Keywords:</strong> migrate reactjs to nextjs, migrate react to next.js, how to migrate react app to next.js</p>
<p>Thanks for reading! If you&#x27;re interested in going the other way around, check out my article on <a href="https://www.jakeprins.com/blog/how-to-convert-nextjs-to-reactjs">how to convert a Next.js application to React.js</a>. For more updates on new projects or articles, follow me on Twitter: <a href="https://twitter.com/jake_prins">@jake_prins</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Build a Sellable Side-Project in 5 Months: From Idea to Acquisition]]></title>
            <link>https://jakeprins.com/blog/how-to-build-a-sellable-side-project-in-5-months-from-idea-to-acquisition</link>
            <guid>https://jakeprins.com/blog/how-to-build-a-sellable-side-project-in-5-months-from-idea-to-acquisition</guid>
            <pubDate>Sun, 13 Aug 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[In just five months, my idea went from concept to acquisition. I’ll share how I did it in this post.]]></description>
            <content:encoded><![CDATA[<p>In just five short months, <a href="https://www.teamsmart.ai/">my idea</a> went from concept to acquisition. This is my second acquisition this year, and I’ll share how I did it in this post.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*Nm1_iC89eUi3Eb0JeVqUPg.jpeg" alt="indiehacker"/></p>
<h2>The beginning of the story</h2>
<p>It all started when the ChaptGPT/AI hype dominating Twitter got the best of me, and I wanted to experiment with whatever all the buzz was about. I decided to build a small project using <a href="https://openai.com/blog/openai-api">The OpenAI API</a> to learn more. I was careful to keep it small and manageable because I already work for <a href="https://apideck.com/">Apideck</a>, have two young children, and spend most of my free time working on <a href="https://universal.page/">universal.page</a>. So basically, I didn’t have the time for this.</p>
<p>For a short period, I was blocked from progressing on universal.page due to some <a href="https://lukso.network/">LUKSO</a> contract updates, so this gave me a little window of time to experiment with AI stuff. That’s when I created <a href="https://www.teamsmart.ai/">TeamSmart AI</a>, a browser extension allowing you to add different AI characters to your team and have them as sidekicks wherever you go. <strong>Within just one month,</strong> <a href="https://twitter.com/jake_prins/status/1645337173548322817"><strong>I made my first sale</strong></a><strong>!</strong></p>
<p>These are the steps I took I believe to have contributed most to the success of this side project.</p>
<h2>1. Launch fast</h2>
<p>I have developed a simple frontend application using <a href="https://nextjs.org/">Next.js</a> without a backend or database (it even just utilized localstorage to store data). For rapid styling, I used <a href="https://tailwindcss.com/">TailwindCSS</a>. I am familiar with this stack, allowing me to build UIs quickly. I released the first version in three weeks, and one week later, I made my first sale. However, I had to refund the customer due to a bug. After resolving the issue, I gained more satisfied customers. Within a month, I was able to validate the idea, which helped me decide whether to invest more time into it.</p>
<h2>2. Build something cool</h2>
<p>This may seem obvious, but it’s crucial to build something that people actually want. Personally, I prioritize creating a visually appealing product with a great user experience. However, quality and speed are often trade-offs, so you must choose which is more important for your project. My approach involves focusing on one feature at a time and making it exceptional before moving on to the next.</p>
<h2>3. Share it everywhere</h2>
<p>I submitted my product to several directories like ProductHunt, Hackernews, Indiehackers, Betalist, and <a href="https://www.makermove.com/directories">others</a>. Typically, this would generate some initial traffic, but this time was different. A surge of interest in AI has led to many AI newsletters and AI directories. I discovered <a href="https://ailaunchlist.com/">a massive list of AI directories</a> and submitted my link to all of them. I spent a full day just submitting my project on these websites. Then, some people started covering my project in newsletters, and all the other AI guys just took over. Mentions on Twitter by big names caused a little buzz on social media and caused a surge in Google searches. I don’t think this is very easy to replicate in a different time or space. It’s just a matter of riding the AI hype train.</p>
<h2>4. Focus on SEO</h2>
<p>I was inspired by <a href="https://twitter.com/dannypostmaa/status/1646368426246680579">Danny Postma’s Twitter thread</a> on SEO and started using <a href="https://ahrefs.com/keyword-generator">Ahrefs</a> to find keywords. I generated separate pages for each AI assistant in my tool. One assistant used the <a href="https://lexica.art/">Lexica API</a> to show AI-generated images. “Lexica” was a highly searched term with low keyword difficulty. After creating a separate page with many keywords for my Lexica integration, I saw a lot of impressions on Google. Although most people just searched for the original website of Lexica, the total number of impressions became so big that the clicks to my Lexica page still outgrew the clicks to my main page.</p>
<h2>5. Selling on Acquire.com</h2>
<p>The product was free to use, but for a one-time $39, you could unlock more AI assistants and features. <a href="https://twitter.com/jake_prins/status/1655189633796390913">My monthly sales quickly reached 2–3K</a>, and since I didn’t plan to focus on this project fully, I decided to list it for sale on <a href="https://acquire.com/">acquire.com</a>. After a month, it attracted a potential buyer who offered the asking price. We closed the deal and transferred assets within just three weeks.</p>
<p>Although the process went very smoothly, I experienced some trouble afterward. I had sold lifetime licenses and had just started an <a href="https://appsumo.com/">AppSumo</a> deal. I recommended the new owner keep giving those customers access to the tool, even after they implemented a subscription-based model. However, previous customers started to lose access and reached out to me for refunds. People were really disappointed and started writing bad reviews (understandably). Next time, I will ensure that these agreements are included in the contract instead of just in the chat.</p>
<p>At the beginning of this year, I sold another side project and was interviewed by <a href="https://twitter.com/agazdecki">Andrew Gazdecki</a>, the founder of <a href="https://acquire.com/">Acquire.com</a>. If you’re interested, you can check it out <a href="https://www.youtube.com/watch?v=ryHOC9vX_c8">here</a> and learn more about the process. Or check out my blog post: <a href="https://www.jakeprins.com/blog/zero-to-acquired">From Zero to Acquired: My Journey to Selling Serverless SaaS</a>.</p>
<h2>Important notes</h2>
<p>If you’re a new indie hacker or developer, keep in mind that I have years of experience. I often reuse code, manage a component library, and have built many projects (even some Chrome extensions). Additionally, the hype surrounding AI certainly contributed to gaining early exposure. Newsletters and AI directories began to mention it, which resulted in some early sales.</p>
<p>However, don’t let that discourage you from building your own project. If you are new to this, I suggest you start small and set realistic goals when beginning your journey. It’s easy to get caught up in the success stories of indie hackers on social media. Don’t let unrealistic expectations discourage you from pursuing your dreams. Great things require patience, time, and effort.</p>
<p>When I first started, I made the mistake of working endlessly on a project that didn’t gain traction, had no business model, and lacked a strategy for attracting users. My following projects got more traction, but I still didn’t profit from most. However, after those experiences, it became relatively easy to spin up something new and make money from it. Experience matters, although learning from the mistakes of others can give you a massive head start.</p>
<p>I hope my story is inspiring to you. If you’re interested in my journey of building the leading platform for the next generation of digital collectibles, follow <a href="https://twitter.com/jake_prins"><strong>me on Twitter</strong></a>. Also, check out <a href="https://universal.page/">universal.page</a> to learn more.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Exploring the Power of NFTs 2.0: Use cases to spark your excitement]]></title>
            <link>https://jakeprins.com/blog/exploring-the-power-of-nfts-2-0-12-use-cases-to-spark-your-excitement</link>
            <guid>https://jakeprins.com/blog/exploring-the-power-of-nfts-2-0-12-use-cases-to-spark-your-excitement</guid>
            <pubDate>Wed, 24 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[In this article, I share 12 use cases for NFTs 2.0 that go beyond digital collectibles and art.]]></description>
            <content:encoded><![CDATA[<h1>Exploring the Power of NFTs 2.0: Use cases to spark your excitement.</h1>
<p>NFTs are digital assets stored on a blockchain to record ownership and authenticity. We have seen a lot of speculation around NFTs, but besides digital collectibles, we haven’t seen that much attention to the many more compelling use cases. With <a href="https://universal.page/blog/what-are-nfts-2-0-and-which-problems-do-they-solve">new NFT standards on the rise</a>, we can expect more innovation in the space in the future. Here are some use cases to be excited about.</p>
<h2>Gaming</h2>
<p>I used to enjoy playing the NBA 2K MyCareer mode frequently. This mode enabled me to create my basketball player, level up their skills, modify their playing style, and earn points to buy in-game apparel. However, it required a lot of effort to progress, and when a new version of the game was released, I had to restart from the beginning. It became increasingly frustrating, mainly when 2K made it more difficult to earn rewards through gameplay and instead heavily promoted the purchase of loot boxes, virtual currency, and in-game items. Items you would lose as soon as the game&#x27;s next version comes out. As a result, many players, like me, are done playing these games.</p>
<p>NFTs could bring a new opportunity for game developers to do this differently. They could offer players a new level of ownership and control over in-game items and have different options to monetize their games, like through royalties of items traded on open marketplaces.</p>
<p>The new <a href="https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset/">LSP8 standard</a> presents an exciting opportunity for game developers to enhance the gaming experience for players. With LSP8 collections, NFTs can now have token IDs that include addresses of other smart contracts. Paired with <a href="https://docs.lukso.tech/standards/lsp-background/erc725/#erc725y-generic-data-keyvalue-store">ERC725Y</a>, a data store designed for on-chain information that can be dynamically updated, this offers developers more flexibility and options to create more advanced NFTs with new features.</p>
<p>Just picture having a game character, like that basketball player in MyCareer, but as an NFT 2.0. Your character would have its own smart contract with an infinite key-value store, allowing it to level up and be customized entirely on-chain. All of its progress and customizations could be stored on the smart contract. Plus, it could even own other in-game items (that are also NFTs). You could transfer or sell your character with all its progress and other owned items whenever you lose interest in the game. This could be revolutionary, especially for role-playing games that require a lot of time and grinding to level up.</p>
<p>NFTs could transform the gaming industry by empowering players to own their in-game assets, earn money from them, and provide game developers with new revenue streams. While there have been a lot of talks about Web3 gaming, we haven’t seen much true success stories yet. It takes a long time to develop a good game, so we have to keep an eye out on the space, but with better NFT standards and increased productivity through AI, it will be interesting to see how gaming will evolve.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*yeFvOKs30XvU1Ec-jBnFzg.jpeg" alt="LSP8 TokenID Type: Address"/>
<a href="https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset/">LSP8 TokenID Type: Address</a></p>
<h2>Traceable Verification Systems</h2>
<p>Sustainability and ethical practices have become increasingly important, especially in the fashion industry. To address these issues effectively, traceable verification systems are necessary. NFTs offer a powerful solution to achieve this goal. They can provide a secure and transparent way to track the lifecycle of fashion products.</p>
<p>By tokenizing garments and accessories using NFTs, brands can create a digital passport that follows these items throughout their lifecycle. This record can be used to verify that products are indeed recycled. It can also incentivize sustainable consumer behavior, such as participating in take-back programs or choosing products made from recycled materials.</p>
<p>An exciting project to follow in this space is <a href="https://www.circulaid.io/">Candour.Digital</a>, a new platform built on top of <a href="https://lukso.network/">LUKSO</a>, is leveraging the most current standards to help fashion brands to prove where &amp; when a product is recycled.</p>
<p>The <a href="https://docs.lukso.tech/standards/lsp-background/erc725/">ERC-725</a> standard offers a dynamic on-chain key-value store, making it suitable for these use cases. The on-chain information gets updated over time to keep track of the current state of an item. Combined with the power of a <a href="https://docs.lukso.tech/guides/key-manager/execute-relay-transactions/">Transaction Relayer Service</a>, companies can offer a seamless user experience in which the user does not need to know about the technical underlies and deal with gas fees. These new offerings and standards can bring new applications that were impossible in the last few years.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*i83ZcQ7rAhRWNTFSgMNEFQ.png" alt=""/>
<a href="https://www.circulaid.io/">https://www.circulaid.io/</a></p>
<h2>Ticketing</h2>
<p>Using NFTs for event ticketing is a great idea. By selling tickets in the form of NFTs, fans are provided with a secure digital asset that creates new opportunities for artists to connect with their fans.</p>
<p>Artists can easily airdrop new tickets or discounts for upcoming events. They can identify their most devoted fans, who attend all their events, and provide them with special treatment. Furthermore, they can even organize exclusive events or content solely for fans who, for example, attended a particular concert.</p>
<p>This use case is already something that is being used right now. For example, Avenged Sevenfold fans holding the artist’s NFTs were allowed to purchase tickets before others through <a href="https://ticketmaster-us.zendesk.com/hc/en-us/articles/9782264993681-What-are-Virtual-Commemorative-Tickets-">Ticketmaster’s integration</a>. This has already shown promising results, with 20% of the fan club members buying tickets with their crypto wallet.</p>
<p>NFTs also allow easy fan-to-fan ticket sales without a middleman. Artists could now also benefit from secondary sales, as they can enable royalties on secondary sales.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*x2djZrSCNe8rkcvjs6-MPg.jpeg" alt=""/>
<a href="https://techcrunch.com/2022/08/31/ticketmaster-taps-the-flow-blockchain-to-let-event-organizers-issue-nfts-tied-to-tickets/">Ticketmaster taps the Flow blockchain to let event organizers issue NFTs tied to tickets | TechCrunch</a></p>
<h2>Music</h2>
<p>NFTs have the potential to revolutionize several aspects of the music industry. For instance, musicians can use NFTs as concert tickets, providing opportunities for long-term engagement with their fans. Additionally, NFTs can be utilized for token-gated music, as demonstrated by the <a href="https://twitter.com/moonbirds/status/1656711516073390081">partnership</a> between <a href="https://opensea.io/collection/proof-moonbirds">Moonbirds</a> and <a href="https://www.spotify.com/">Spotify</a>. Moonbirds holders can access the Moonbirds playlist by connecting their wallets, creating a new experience with a famous Web2 brand.</p>
<p>Moreover, NFTs can help upcoming and indie musicians cultivate a dedicated fan base and earn a living. For example, <a href="https://www.sound.xyz/">Sound.xyz</a> is one platform that supports artists using web3-native music and economic tools, including NFTs, to enhance artist-fan connections and create unique listening experiences. Artists can sell limited edition NFTs of their songs, and buyers can receive special community access as perks.</p>
<p>As AI-generated music emerges, artists could allow their voices to be used in AI-generated songs, provided they receive royalties. NFTs 2.0 can store dynamic data and efficiently manage flexible royalties settings on-chain. The NFT 2.0 marketplace <a href="https://universal.page/">Universal Page</a> provides a simple user interface for specifying different receiving addresses and their corresponding percentages. These flexible on-chain settings are ideal for handling AI-generated music royalties, allowing artists to “rent out” their voice while ensuring fair compensation for all creators involved.</p>
<p>NFTs enable artists and song creators to connect with their audience, offer rewards, and handle royalties. As AI-generated music gains popularity in this world with numerous intermediaries, NFTs could play a critical role in the future of the music industry.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*2MihAWWqkOEkM5W9f5QDoA.png" alt="Universal Page Royalties"/>
Flexible Royalty Settings on <a href="https://universal.page/">Universal Page</a></p>
<h2>Advertising and sponsorships</h2>
<p>NFTs provide a new way for companies to engage with their audience and promote their brand. Companies can create branded NFTs representing their products or services, which can be used as a promotional tool or as a reward for loyal customers.</p>
<p>Let’s say a sports team is selling NFT tickets representing game entry. The team could partner with companies to offer sponsored NFTs that provide additional benefits to ticket holders, such as access to a VIP lounge or a meet and greet with team members.</p>
<p>A sports apparel company could sponsor a limited edition NFT that gives the ticket holder a discount on their products. The NFT could feature the company’s logo or branding and could be sold as a limited edition collectible. This would promote the apparel company’s products to sports fans and provide an additional revenue stream for the sports team.</p>
<p>The same would work for a music festival that sells NFT tickets. The festival organizers could partner with companies to offer sponsored NFTs that provide additional benefits to ticket holders, such as access to exclusive areas or merchandise. For example, a beverage company could sponsor a limited edition NFT that gives the ticket holder a free drink at the festival.</p>
<p>NFTs that feature branding are visible to both their owners and their connections. The emergence of Web3 social apps makes it easy to view the NFTs of your friends. In addition, if you have a personalized <a href="https://universal.page/">Universal Page</a> showcasing all of your digital assets, anyone who visits your page can view your branded NFT, resulting in increased visibility.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*31sEaUvqW45IV3ZdfXOiBg.png" alt=""/>
<a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fbueno.art%2Fblog%2Fbig-brands-nfts&amp;psig=AOvVaw2ndzFEIftF6AZd_zkHvgiA&amp;ust=1684128160527000&amp;source=images&amp;cd=vfe&amp;ved=0CBMQjhxqFwoTCJDl4reI9P4CFQAAAAAdAAAAABBV">Louis Vuitton to Budweiser: 6 Big Brands Breaking into NFTs | Bueno Blog</a></p>
<h2>Evolving Digital Art</h2>
<p>NFTs have revolutionized the world of Digital Art, making it easier for creators to sell their work and generating new revenue streams for struggling artists. The most well-known example of this success is digital artist <a href="https://twitter.com/beeple">Beeple</a>, who made a record-breaking $69 million at a Christie’s auction.</p>
<p>With new standards on the rise and the power of AI-generated art, it will be fascinating to see what digital artists create. With <a href="https://docs.lukso.tech/standards/lsp-background/erc725/">ERC-725Y</a>, contracts have an infinite key-value store to store information and new functions to update that data store.</p>
<p>With more flexibility and upgradability, imagine generative art that changes based on the number of holders or art that evolves after specific events. Think of a digital fashion item that varies based on the season or a music album that adds new songs over time. Think of <a href="https://universal.page/blog/flexible-royalties">royalty settings</a> that adjust based on trading volume or your profile picture used in AI-generated art. The possibilities for fun mini-games, cool collectibles, or profound artworks are exciting.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*YLhlAN3LTdXbfSvbQlEkSw.jpeg" alt="Beeple’s “Everydays: the First 5000 Days”"/></p>
<h2>Communities and communication</h2>
<p>Many NFT communities were initially formed on <a href="https://discord.com/">Discord</a> servers by holders of specific NFT collections, with the promise of “going to the moon.” However, as many prices have dropped and founders lost interest, these servers have become less active. Despite this, some NFT communities remain active and promising, but creating exclusive Discord servers for NFT holders seems to be just the beginning of leveraging blockchains for community building.</p>
<p>There is much room for growth, and leveraging <a href="https://docs.lukso.tech/standards/universal-profile/introduction/">Universal Profiles</a> and their built-in permission systems could be the missing key in Web3 messaging. Just like you don’t want your email to be public for everyone, you also don’t want anyone that knows your wallet address to be able to message you. Managing permissions in your Universal Profile looks pretty suitable with a Web3 messaging protocol like <a href="https://xmtp.org/">XMTP</a>.</p>
<p>NFTs could play a part in improving the user experience of managing these types of permissions. Instead of adding each allowed address one by one and specifying what permissions they should get, you could authorize everyone that is part of the same DAO or community. Or you could create special NFTs for your friend group to give them instant permission to send you messages.</p>
<p>We can expect to see much better tools for organizing Web3 communication, and it will be exciting to see the role of social dApps and chat platforms in building new communities.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*_c5I7kNFi5sFoTqpsthimQ.jpeg" alt=""/>
<a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fwww.launchcaster.xyz%2Fx%2Ffarcaster%2F6401ee39affa1508e8f7533c&amp;psig=AOvVaw2C-_cm10PLDybD1ECNIznc&amp;ust=1683999385878000&amp;source=images&amp;cd=vfe&amp;ved=0CBMQjhxqFwoTCMjBptuo8P4CFQAAAAAdAAAAABAE">“Open protocol and network for secure web3 messaging — XMTP.”</a></p>
<h2>Certification and achievements</h2>
<p>Online courses and certificates are difficult to verify as they are stored in various systems. For example, an individual can claim to have completed a course and display a badge on their LinkedIn profile, but there is no way to verify its authenticity. However, if the badge is an NFT 2.0, one can easily see who issued it and confirm its validity.</p>
<p>In a previous software development job, we used a gamification tool to award points and badges for achievements for completed work. It was a fun way to show and receive recognition, but unfortunately, these achievements would only be valuable in that closed system. They could not be shared with future employers. If they were NFTs, they would have retained their value even outside the organization and could have been helpful in my career progression.</p>
<p>Companies can now issue NFTs to their employees as digital certificates for completing courses or achieving certain milestones. These NFTs can prove an individual’s professional capabilities and be used for verification.</p>
<p>NFTs can also validate personal branding by representing accomplishments, endorsements, or testimonials. They can even be used just to express thoughts on specific topics. <a href="https://www.coinbase.com/">Coinbase</a>, for instance, has launched a <a href="https://twitter.com/coinbase/status/1650612986980048897">“Stand with Crypto NFT”</a> free-to-mint digital asset that people can use to show their support. This NFT is not meant for trading and has no intended use other than as a commemorative item. Its purpose is to demonstrate that you are part of a growing community that has faith in the future of cryptocurrency.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*a6zfpErcdd_Bk5fSOCYtUA.jpeg" alt=""/>
<a href="https://twitter.com/coinbase/status/1650612986980048897">Coinbase️ on Twitter</a></p>
<h2>Phygitals</h2>
<p>By establishing digital identities for physical products, creators can enable digital ownership, continuously gather their unique and singular data history, and have lasting and direct access to their owners.</p>
<p>Phygitals, or physical-digital hybrids, are a new type of NFT that combine physical goods with digital ownership. This could include fashion items, where the physical item is shipped to the buyer, but the ownership and authenticity are recorded on the blockchain. This opens up new possibilities for brands to create limited-edition collections authenticated through NFTs and sold as high-value collectibles. Digital fashion is an emerging trend in the NFT space. Designers create digital clothing items that can be worn in virtual environments like video games, social media, and virtual/augmented reality experiences.</p>
<p>For example, Nike uses NFTs to authenticate and sell limited-edition sneakers and other physical goods. This represents a new frontier for NFTs and could bring the technology into the mainstream.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*wOWfTAkwhNMiBfg6dOk_Ng.png" alt="Nike, RTFKT"/>
<a href="https://www.google.com/url?sa=i&amp;url=https%3A%2F%2Fwww.highsnobiety.com%2Fp%2Fnike-rtfkt-tiffany-gucci-nft-ranking-sales%2F&amp;psig=AOvVaw1kI0b44-Axar_8ndi5JyY5&amp;ust=1683998363384000&amp;source=images&amp;cd=vfe&amp;ved=0CBMQjhxqFwoTCPj85vOk8P4CFQAAAAAdAAAAABAE">Nike, RTFKT, Tiffany &amp; Gucci Top NFT Profit Rankings: Full List</a></p>
<h2>Tokenize real-world assets</h2>
<p>As seen earlier in this article, we can make tracking the lifecycle of physical products more transparent and secure by utilizing NFT 2.0 standards. The same can be expected for tracking ownership of physical assets. This often involves many middlemen; some could soon be replaced by a trusted decentralized network that offers a transparent ledger.</p>
<p>However, this is a bit tricky since you’re dealing with both the transfer of the ownership (on-chain) and the physical asset. You can’t buy an NFT of a car on <a href="https://universal.page/">Universal Page</a> and expect it to arrive at your doorstep magically. Therefore, we will always see some intermediaries involved in the transaction of physical assets. But, tracking ownership for real-world assets would be much more transparent when done on a decentralized network. It would involve fewer people (who take a cut of the transaction), making it more cost-effective. We have already seen <a href="https://blockworks.co/news/a-real-estate-backed-nft-sold-for-653000">real estate being sold using an NFT</a>, demonstrating the potential of tokenized assets. With more flexible standards and a better on-chain reputation system, we’ll see the process of tokenized assets improving and potentially transforming how we buy and sell property.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*VMm4_BB_FESEgA-Mxqzeiw.png" alt=""/>
<a href="https://www.entrepreneur.com/business-news/the-first-nft-home-just-sold-for-175000/437522">The First NFT Home Just Sold for $175,000 | Entrepreneur</a></p>
<h2>Profiles, websites &amp; usernames</h2>
<p>NFTs could power domains, profiles, and usernames. With Web3 social apps, you own your username and can easily switch from one app to another and bring all your data. A powerful example is the <a href="https://docs.lens.xyz/docs/profile">Lens Profile NFT</a>, the main object in the <a href="https://www.lens.xyz/">Lens Protocol</a>. The NFT contains the history of all posts, <a href="https://docs.lens.xyz/docs/mirror">mirrors</a>, <a href="https://docs.lens.xyz/docs/comment">comments</a>, and other content you generate. Plus, it records on-chain relationships.</p>
<p>A more powerful example of on-chain profiles is <a href="https://docs.lukso.tech/standards/universal-profile/introduction">Universal Profiles</a>, although it is important to note that it differs from an NFT or NFT Profile. Universal Profiles are made of a collection of smart contracts that serve as your account and offer a multitude of features. Additionally, it doubles as a wallet, allowing it to own tokens and NFTs.</p>
<p>We’ve also seen a lot of adoption of <a href="https://ens.domains/">ENS</a> (Ethereum Name Service). This unique digital asset represents the ownership of a decentralized human-readable domain name for wallets, websites, &amp; more.</p>
<p>ENS allows users to create or purchase domain names ending in .eth, which can be used to identify their cryptocurrency wallet addresses, decentralized websites, or decentralized applications (dApps). These domain names are represented as NFTs, which ensures their uniqueness, ownership, and transferability within the Ethereum ecosystem.</p>
<p>Another example is the <a href="https://universal.page/blog/universal-page-name">Universal Page Name</a> (UPN), an NFT that users can mint to create their personal website or NFT storefront.</p>
<p>This NFT uses the LSP8 standard with the token ID type `string`. This means the unique name you choose is stored inside the token ID. You fully own that name and can easily sell or transfer it. And when you do, the new owner’s Universal Profile details will appear on the customized page. This makes creating, customizing, and trading personal websites easy without any middlemen involved.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*4odphsuAcQoZX-CHWg-lzA.png" alt="Universal Page Name"/>
<a href="https://universal.page/">Universal Page</a></p>
<h2>Digital Fashion</h2>
<p>The fashion industry is undergoing a digital revolution that challenges traditional business models. One exciting development in digital fashion is using NFTs to create unique clothing pieces that can be owned and traded like physical items.</p>
<p>This opens up exciting possibilities for fashion enthusiasts — imagine owning a one-of-a-kind digital dress that can be worn in virtual reality or displayed on social media. Similarly, you could purchase a limited edition digital t-shirt from your favorite band’s online store.</p>
<p>While our digital lives and identities become increasingly important, it makes sense to see that people start to care more about their online appearances. Everything we care about in real life moves slowly to the virtual worlds of the web. People who use fashion to express themselves, to stand out, or to flex will also want to do that in their digital lives, and NFTs are the perfect tool for that.</p>
<p><a href="https://thedematerialised.com/">The Dematerialised</a>, an experimental NFT marketplace on LUKSO, is the space leader of Digital Fashion, working with big brands like <a href="https://www.karl.com/">Karl Lagerfield</a>, <a href="https://rtfkt.com/">RTFKT</a>, and many more.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/0*cpEehzUaXlsfIp3C.jpg" alt="Digital Fashion: The Dematerialised"/>
<a href="https://www.highsnobiety.com/p/mcq-the-dematerialised-nft-collection/">MCQ is Launching Its First NFT Collection on THE DEMATERIALISED</a></p>
<h2>Conclusion</h2>
<p>In conclusion, the potential of <a href="https://www.jakeprins.com/blog/what-are-nfts-2-0-and-which-problems-do-they-solve">NFTs 2.0</a> goes far beyond just digital collectibles and art. It’s clear that with the rise of new standards like <a href="https://docs.lukso.tech/standards/smart-contracts/lsp8-identifiable-digital-asset">LSP8</a> and <a href="https://docs.lukso.tech/standards/smart-contracts/erc725-contract/">ERC-725</a>, a wide range of applications and use cases are emerging. From revolutionizing the gaming industry to traceable verification systems in fashion, ticketing, music, digital art, and the tokenization of real-world assets, NFTs are paving the way for innovations. Moreover, the transformation of communities, communication, and certifications through NFTs highlights the numerous possibilities to create value and change the way we live and interact with each other in the digital world.</p>
<p>As the NFT space matures, I’m looking forward to witnessing the growth of these great use cases and the new ideas they inspire.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to create a ChatGPT application using Next.js and the OpenAI API.]]></title>
            <link>https://jakeprins.com/blog/how-to-create-a-chatgpt-application-using-next-js-and-the-openai-api</link>
            <guid>https://jakeprins.com/blog/how-to-create-a-chatgpt-application-using-next-js-and-the-openai-api</guid>
            <pubDate>Sat, 01 Apr 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Build your own ChatGPT application with Next.js, TypeScript, and TailwindCSS.]]></description>
            <content:encoded><![CDATA[<h2>Build a ChatGPT application with Next.js, TypeScript, and TailwindCSS.</h2>
<h3>Prerequisites</h3>
<ul>
<li>Node.js and npm installed on your machine</li>
<li>A basic understanding of React and TypeScript</li>
<li>An OpenAI API key — you can sign up for an account and generate an API key from the <a href="https://platform.openai.com/account/api-keys">OpenAI website</a>.</li>
</ul>
<blockquote>
<p>You can view my complete project by visiting <a href="https://www.teamsmart.ai">TeamSmart.AI</a>. It&#x27;s a Chrome extension that lets you choose from a variety of AI assistants to aid you with your everyday tasks.</p>
</blockquote>
<h3>What we will build</h3>
<p>Following this tutorial, we will use the OpenAI API to create a simple chat application like ChatGPT.</p>
<p><img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*1ALWm32Mtdble4nh4TDfFA.jpeg" alt="ChatGPT Tutorial Application"/></p>
<h2>Step 1: Setting up the project</h2>
<p>We will use the <a href="https://github.com/apideck-io/next-starter-kit">Next.js Starter Kit</a> from <a href="https://apideck.com/">Apideck</a> to set up our project. It comes with TypeScript, TailwindCSS, and the <a href="https://developers.apideck.com/components">Apideck Components</a> library pre-installed.</p>
<ol>
<li>Create a new project using the command line:</li>
</ol>
<pre><code>yarn create-next-app --example https://github.com/apideck-io/next-starter-kit
</code></pre>
<ol start="2">
<li>Choose your project name and navigate to your new folder. Inside, create a <code>.env.local</code> file at the root of your project and add the following line (replacing <code>YOUR_OPENAI_API_KEY</code> with your actual key):</li>
</ol>
<pre class="language-bash"><code class="language-bash"><span class="token assign-left variable">OPENAI_API_KEY</span><span class="token operator">=</span>YOUR_OPENAI_API_KEY
</code></pre>
<h2>Step 2: Writing the API client</h2>
<p>To not expose your OpenAI API key, we will create an API endpoint instead of making requests to the API directly from our browser. Follow these steps to set up your endpoint using a <a href="https://nextjs.org/docs/api-routes/introduction">Next.js API route</a>:</p>
<ol>
<li>Locate the pages folder of your project and create a new subfolder named <code>api</code>.</li>
<li>Inside the <code>api</code>folder, create a new TypeScript file named <code>createMessage.ts</code></li>
<li>In the <code>createMessage.ts</code> file, we can either use the OpenAI SDK or issue an HTTP request to the OpenAI API to generate a new message for our “conversation” with the AI. We will use the direct API call in this tutorial.</li>
</ol>
<p>Here is the code for our API route.</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">NextApiRequest</span><span class="token punctuation">,</span> <span class="token maybe-class-name">NextApiResponse</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next&#x27;</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">createMessage</span><span class="token punctuation">(</span>
  req<span class="token operator">:</span> <span class="token maybe-class-name">NextApiRequest</span><span class="token punctuation">,</span>
  res<span class="token operator">:</span> <span class="token maybe-class-name">NextApiResponse</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> messages <span class="token punctuation">}</span> <span class="token operator">=</span> req<span class="token punctuation">.</span><span class="token property-access">body</span>
  <span class="token keyword">const</span> apiKey <span class="token operator">=</span> process<span class="token punctuation">.</span><span class="token property-access">env</span><span class="token punctuation">.</span><span class="token constant">OPENAI_API_KEY</span>
  <span class="token keyword">const</span> url <span class="token operator">=</span> <span class="token string">&#x27;https://api.openai.com/v1/chat/completions&#x27;</span>

  <span class="token keyword">const</span> body <span class="token operator">=</span> <span class="token known-class-name class-name">JSON</span><span class="token punctuation">.</span><span class="token method function property-access">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    messages<span class="token punctuation">,</span>
    model<span class="token operator">:</span> <span class="token string">&#x27;gpt-3.5-turbo&#x27;</span><span class="token punctuation">,</span>
    stream<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      method<span class="token operator">:</span> <span class="token string">&#x27;POST&#x27;</span><span class="token punctuation">,</span>
      headers<span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token string-property property">&#x27;Content-Type&#x27;</span><span class="token operator">:</span> <span class="token string">&#x27;application/json&#x27;</span><span class="token punctuation">,</span>
        <span class="token maybe-class-name">Authorization</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Bearer </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>apiKey<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      body<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword control-flow">await</span> response<span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    res<span class="token punctuation">.</span><span class="token method function property-access">status</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> data <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword control-flow">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    res<span class="token punctuation">.</span><span class="token method function property-access">status</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> error<span class="token operator">:</span> error<span class="token punctuation">.</span><span class="token property-access">message</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>For this example, we’re utilizing the <code>gpt-3.5-turbo</code> model, as it’s currently available at the time of writing. If you possess access to GPT-4, you can alter the value as necessary.</p>
<p>The <code>messages</code> value is an array that stores messages from our chat-based conversation with the AI. Each message contains both a <code>role</code> and <code>content</code>. The <code>role</code> can be either:</p>
<ul>
<li><strong><em>system</em></strong>
This is the initial prompt sent to the AI, which gives it instructions on how to behave. For example, you may use “You are ChatGPT, a language model trained by OpenAI.” or “You are a software engineer who develops software programs, web applications, and mobile applications using various programming languages and development tools.” Experimenting with different initial system messages can help you fine-tune the AI’s behavior.</li>
<li><strong><em>user</em></strong>
This represents the user’s input. For example, the user could ask, “Can you provide a JavaScript function fetching the current weather?”</li>
<li><strong><em>assistant</em></strong>
This is the AI’s response, which the API endpoint will return.</li>
</ul>
<h2>Step 3: Creating the messaging functions</h2>
<p>Now that the endpoint is ready to interface with the AI, we can begin designing our user interface to facilitate interaction. To start, we’ll create the <code>sendMessage</code> function. Here’s how:</p>
<ol>
<li>Create a new file named <code>sendMessage.ts</code> in the <code>utils</code> folder.</li>
<li>Add the following code to <code>sendMessage.ts</code>:</li>
</ol>
<pre class="language-ts"><code class="language-ts"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;openai&#x27;</span>

<span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">sendMessage</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>messages<span class="token operator">:</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">try</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">&#x27;/api/createMessage&#x27;</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
      method<span class="token operator">:</span> <span class="token string">&#x27;POST&#x27;</span><span class="token punctuation">,</span>
      headers<span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token string-property property">&#x27;Content-Type&#x27;</span><span class="token operator">:</span> <span class="token string">&#x27;application/json&#x27;</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      body<span class="token operator">:</span> <span class="token known-class-name class-name">JSON</span><span class="token punctuation">.</span><span class="token method function property-access">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span> messages <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> response<span class="token punctuation">.</span><span class="token method function property-access">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword control-flow">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>With this function in place, you can establish communication between the user interface and the AI via the API endpoint.</p>
<p>Now let’s setup the logic to create new messages inside a <code>useMessages</code> hook. Inside the <code>utils</code> folder, create a file named <code>useMessages.ts</code> and add the following code:</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useToast <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;@apideck/components&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;openai&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span>
  <span class="token maybe-class-name">ReactNode</span><span class="token punctuation">,</span>
  createContext<span class="token punctuation">,</span>
  useContext<span class="token punctuation">,</span>
  useEffect<span class="token punctuation">,</span>
  useState<span class="token punctuation">,</span>
<span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> sendMessage <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;./sendMessage&#x27;</span>

<span class="token keyword">interface</span> <span class="token class-name"><span class="token maybe-class-name">ContextProps</span></span> <span class="token punctuation">{</span>
  messages<span class="token operator">:</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span><span class="token punctuation">[</span><span class="token punctuation">]</span>
  <span class="token function-variable function">addMessage</span><span class="token operator">:</span> <span class="token punctuation">(</span>content<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token known-class-name class-name">Promise</span><span class="token operator">&lt;</span><span class="token keyword">void</span><span class="token operator">&gt;</span>
  isLoadingAnswer<span class="token operator">:</span> <span class="token builtin">boolean</span>
<span class="token punctuation">}</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">ChatsContext</span> <span class="token operator">=</span> <span class="token generic-function"><span class="token function">createContext</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token maybe-class-name">Partial</span><span class="token operator">&lt;</span><span class="token maybe-class-name">ContextProps</span><span class="token operator">&gt;&gt;</span></span></span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword module">export</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">MessagesProvider</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span> children <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token punctuation">{</span> children<span class="token operator">:</span> <span class="token maybe-class-name">ReactNode</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> addToast <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useToast</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>messages<span class="token punctuation">,</span> setMessages<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token generic-function"><span class="token function">useState</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token maybe-class-name">ChatCompletionRequestMessage</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>isLoadingAnswer<span class="token punctuation">,</span> setIsLoadingAnswer<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span>

  <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token function-variable function">initializeChat</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> systemMessage<span class="token operator">:</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
        role<span class="token operator">:</span> <span class="token string">&#x27;system&#x27;</span><span class="token punctuation">,</span>
        content<span class="token operator">:</span> <span class="token string">&#x27;You are ChatGPT, a large language model trained by OpenAI.&#x27;</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">const</span> welcomeMessage<span class="token operator">:</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
        role<span class="token operator">:</span> <span class="token string">&#x27;assistant&#x27;</span><span class="token punctuation">,</span>
        content<span class="token operator">:</span> <span class="token string">&#x27;Hi, How can I help you today?&#x27;</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
      <span class="token function">setMessages</span><span class="token punctuation">(</span><span class="token punctuation">[</span>systemMessage<span class="token punctuation">,</span> welcomeMessage<span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// When no messages are present, we initialize the chat the system message and the welcome message</span>
    <span class="token comment">// We hide the system message from the user in the UI</span>
    <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>messages<span class="token operator">?.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">initializeChat</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>messages<span class="token operator">?.</span>length<span class="token punctuation">,</span> setMessages<span class="token punctuation">]</span><span class="token punctuation">)</span>

  <span class="token keyword">const</span> <span class="token function-variable function">addMessage</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>content<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">setIsLoadingAnswer</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
    <span class="token keyword control-flow">try</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> newMessage<span class="token operator">:</span> <span class="token maybe-class-name">ChatCompletionRequestMessage</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
        role<span class="token operator">:</span> <span class="token string">&#x27;user&#x27;</span><span class="token punctuation">,</span>
        content<span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">const</span> newMessages <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token spread operator">...</span>messages<span class="token punctuation">,</span> newMessage<span class="token punctuation">]</span>

      <span class="token comment">// Add the user message to the state so we can see it immediately</span>
      <span class="token function">setMessages</span><span class="token punctuation">(</span>newMessages<span class="token punctuation">)</span>

      <span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">sendMessage</span><span class="token punctuation">(</span>newMessages<span class="token punctuation">)</span>
      <span class="token keyword">const</span> reply <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token property-access">choices</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token property-access">message</span>

      <span class="token comment">// Add the assistant message to the state</span>
      <span class="token function">setMessages</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token spread operator">...</span>newMessages<span class="token punctuation">,</span> reply<span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword control-flow">catch</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// Show error when something goes wrong</span>
      <span class="token function">addToast</span><span class="token punctuation">(</span><span class="token punctuation">{</span> title<span class="token operator">:</span> <span class="token string">&#x27;An error occurred&#x27;</span><span class="token punctuation">,</span> type<span class="token operator">:</span> <span class="token string">&#x27;error&#x27;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span> <span class="token keyword control-flow">finally</span> <span class="token punctuation">{</span>
      <span class="token function">setIsLoadingAnswer</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">ChatsContext.Provider</span></span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> messages<span class="token punctuation">,</span> addMessage<span class="token punctuation">,</span> isLoadingAnswer <span class="token punctuation">}</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">ChatsContext.Provider</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">useMessages</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token function">useContext</span><span class="token punctuation">(</span><span class="token maybe-class-name">ChatsContext</span><span class="token punctuation">)</span> <span class="token keyword module">as</span> <span class="token maybe-class-name">ContextProps</span>
<span class="token punctuation">}</span>
</code></pre>
<h2>Step 4: Implement the message UI components</h2>
<p>Having set up our functions, we can now design UI components that will use these functions to create an interactive chat interface. Follow the steps below:</p>
<ol>
<li>Create a new file called <code>MessageForm.tsx</code> in the <code>components</code> folder of your project and add the following code:</li>
</ol>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Button</span><span class="token punctuation">,</span> <span class="token maybe-class-name">TextArea</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;@apideck/components&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useState <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useMessages <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;utils/useMessages&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">MessageForm</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>content<span class="token punctuation">,</span> setContent<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">&#x27;&#x27;</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> addMessage <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useMessages</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">const</span> <span class="token function-variable function">handleSubmit</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    e<span class="token operator">?.</span><span class="token method function property-access">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token function">addMessage</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span>
    <span class="token function">setContent</span><span class="token punctuation">(</span><span class="token string">&#x27;&#x27;</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span>
      <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>relative mx-auto max-w-3xl rounded-t-xl<span class="token punctuation">&quot;</span></span>
      <span class="token attr-name">onSubmit</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>handleSubmit<span class="token punctuation">}</span></span>
    <span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span> supports-backdrop-blur:bg-white/95 h-[130px] rounded-t-xl border-t border-l border-r border-gray-200 border-gray-500/10 bg-white p-5 backdrop-blur dark:border-gray-50/[0.06]<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">htmlFor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>content<span class="token punctuation">&quot;</span></span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>sr-only<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          Your message
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">TextArea</span></span>
          <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>content<span class="token punctuation">&quot;</span></span>
          <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>Enter your message here...<span class="token punctuation">&quot;</span></span>
          <span class="token attr-name">rows</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">3</span><span class="token punctuation">}</span></span>
          <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>content<span class="token punctuation">}</span></span>
          <span class="token attr-name">autoFocus</span>
          <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>border-0 !p-3 text-gray-900 shadow-none ring-1 ring-gray-300/40 backdrop-blur focus:outline-none focus:ring-gray-300/80 dark:bg-gray-800/80 dark:text-white dark:placeholder-gray-400 dark:ring-0<span class="token punctuation">&quot;</span></span>
          <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">setContent</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token property-access">target</span><span class="token punctuation">.</span><span class="token property-access">value</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span>
        <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>absolute right-8 bottom-10<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>flex space-x-3<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Button</span></span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span><span class="token punctuation">&quot;</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token attr-name">size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>small<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
              Send
              </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span>
                <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>http://www.w3.org/2000/svg<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>none<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>0 0 24 24<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">strokeWidth</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">1.5</span><span class="token punctuation">}</span></span>
                <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>currentColor<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>ml-1 h-4 w-4<span class="token punctuation">&quot;</span></span>
              <span class="token punctuation">&gt;</span></span><span class="token plain-text">
                </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span>
                  <span class="token attr-name">strokeLinecap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>round<span class="token punctuation">&quot;</span></span>
                  <span class="token attr-name">strokeLinejoin</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>round<span class="token punctuation">&quot;</span></span>
                  <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5<span class="token punctuation">&quot;</span></span>
                <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
              </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Button</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">MessageForm</span>
</code></pre>
<p>Now that we have set up the message UI components, we need to create the component to render the list of messages.</p>
<ol start="2">
<li>Create a new file called <code>MessagesList.tsx</code> in the <code>components</code> folder and add the following code:</li>
</ol>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useMessages <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;utils/useMessages&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">MessagesList</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> messages<span class="token punctuation">,</span> isLoadingAnswer <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useMessages</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mx-auto max-w-3xl pt-8<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token punctuation">{</span>messages<span class="token operator">?.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> i<span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> isUser <span class="token operator">=</span> message<span class="token punctuation">.</span><span class="token property-access">role</span> <span class="token operator">===</span> <span class="token string">&#x27;user&#x27;</span>
        <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>message<span class="token punctuation">.</span><span class="token property-access">role</span> <span class="token operator">===</span> <span class="token string">&#x27;system&#x27;</span><span class="token punctuation">)</span> <span class="token keyword control-flow">return</span> <span class="token keyword null nil">null</span>
        <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
            <span class="token attr-name">id</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">message-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>i<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">}</span></span>
            <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">fade-up mb-4 flex </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
              isUser <span class="token operator">?</span> <span class="token string">&#x27;justify-end&#x27;</span> <span class="token operator">:</span> <span class="token string">&#x27;justify-start&#x27;</span>
            <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>i <span class="token operator">===</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">&#x27;max-w-md&#x27;</span> <span class="token operator">:</span> <span class="token string">&#x27;&#x27;</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">}</span></span>
            <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>message<span class="token punctuation">.</span><span class="token property-access">content</span><span class="token punctuation">}</span></span>
          <span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token punctuation">{</span><span class="token operator">!</span>isUser <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>
              <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span>
                <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>https://www.teamsmart.ai/next-assets/team/ai.jpg<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>h-9 w-9 rounded-full<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>avatar<span class="token punctuation">&quot;</span></span>
              <span class="token punctuation">/&gt;</span></span>
            <span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
              <span class="token attr-name">style</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> maxWidth<span class="token operator">:</span> <span class="token string">&#x27;calc(100% - 45px)&#x27;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span>
              <span class="token attr-name">className</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">group relative rounded-lg px-3 py-2 </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
                isUser
                  <span class="token operator">?</span> <span class="token string">&#x27;from-primary-700 to-primary-600 mr-2 bg-gradient-to-br text-white&#x27;</span>
                  <span class="token operator">:</span> <span class="token string">&#x27;ml-2 bg-gray-200 text-gray-700 dark:bg-gray-800 dark:text-gray-200&#x27;</span>
              <span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">}</span></span>
            <span class="token punctuation">&gt;</span></span><span class="token plain-text">
              </span><span class="token punctuation">{</span>message<span class="token punctuation">.</span><span class="token property-access">content</span><span class="token punctuation">.</span><span class="token method function property-access">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token punctuation">{</span>isUser <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>
              <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span>
                <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>https://www.teamsmart.ai/next-assets/profile-image.png<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>h-9 w-9 cursor-pointer rounded-full<span class="token punctuation">&quot;</span></span>
                <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>avatar<span class="token punctuation">&quot;</span></span>
              <span class="token punctuation">/&gt;</span></span>
            <span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token punctuation">)</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">
      </span><span class="token punctuation">{</span>isLoadingAnswer <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mb-4 flex justify-start<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span>
            <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>https://www.teamsmart.ai/next-assets/team/ai.jpg<span class="token punctuation">&quot;</span></span>
            <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>h-9 w-9 rounded-full<span class="token punctuation">&quot;</span></span>
            <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>avatar<span class="token punctuation">&quot;</span></span>
          <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>loader relative ml-2 flex items-center justify-between space-x-1.5 rounded-full bg-gray-200 p-2.5 px-4 dark:bg-gray-800<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>block h-3 w-3 rounded-full<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>block h-3 w-3 rounded-full<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
            </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>block h-3 w-3 rounded-full<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
      <span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">MessagesList</span>
</code></pre>
<p>We do not want to show the initial system message, so if the <code>role</code> is <code>system</code> we return <code>null</code>. Next, we adjust the styling of the messages based on if the <code>role</code> is either <code>assisten</code> or <code>user</code>.</p>
<p>While we are waiting for a response, we show a loading element. For this <code>loader</code> element to animate, we need to add some custom CSS. Inside the styles folder, create a <code>globals.css</code> file and add the following:</p>
<pre class="language-css"><code class="language-css"><span class="token selector"><span class="token class">.loader</span> span</span> <span class="token punctuation">{</span>
  <span class="token property">animation-name</span><span class="token punctuation">:</span> bounce<span class="token punctuation">;</span>
  <span class="token property">animation-duration</span><span class="token punctuation">:</span> <span class="token number">1.5</span><span class="token unit">s</span><span class="token punctuation">;</span>
  <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span>
  <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> ease-in-out<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.loader</span> span<span class="token pseudo-class">:nth-child</span><span class="token punctuation">(</span><span class="token n-th"><span class="token number">2</span></span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  <span class="token property">animation-delay</span><span class="token punctuation">:</span> <span class="token number">50</span><span class="token unit">ms</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.loader</span> span<span class="token pseudo-class">:nth-child</span><span class="token punctuation">(</span><span class="token n-th"><span class="token number">3</span></span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
  <span class="token property">animation-delay</span><span class="token punctuation">:</span> <span class="token number">150</span><span class="token unit">ms</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Make sure to import the CSS file into your <code>_app.tsx</code> file:</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token string">&#x27;styles/globals.css&#x27;</span>
<span class="token keyword module">import</span> <span class="token string">&#x27;styles/tailwind.css&#x27;</span>

<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">ToastProvider</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;@apideck/components&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">AppProps</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next/app&#x27;</span>
<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">App</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token maybe-class-name">Component</span><span class="token punctuation">,</span> pageProps <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">AppProps</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">JSX</span><span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">Element</span></span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">ToastProvider</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Component</span></span> <span class="token spread"><span class="token punctuation">{</span><span class="token spread operator">...</span>pageProps<span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">ToastProvider</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<ol start="3">
<li>We can use our message UI components in our application now that we have built them. Locate the <code>pages</code> directory and open <code>index.tsx</code>. Remove the boilerplate code in this file.</li>
</ol>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Layout</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;components/Layout&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">MessageForm</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;components/MessageForm&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">MessagesList</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;components/MessageList&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">NextPage</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">MessagesProvider</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;utils/useMessages&#x27;</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">IndexPage</span><span class="token operator">:</span> <span class="token function-variable function"><span class="token maybe-class-name">NextPage</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">MessagesProvider</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Layout</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">MessagesList</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>fixed bottom-0 right-0 left-0<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">MessageForm</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Layout</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">MessagesProvider</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">IndexPage</span>
</code></pre>
<p>We have wrapped our components with the <code>MessageProvider</code> so we can share the state between the components. We also added a container div to the <code>MessageForm</code> component, so it has a fixed position at the bottom of the page.</p>
<h2>Step 5: Running the chat application</h2>
<p>Nice job! We now reached the point where we could see our chat application in action. Here’s how you can test your ChatGPT application:</p>
<ol>
<li>Make sure your development server is running. (<code>yarn dev</code>)</li>
<li>Navigate to the root URL of your application in your browser. (<code>localhost:3000</code>)</li>
<li>You should see the UI rendered on the screen. Type a message in the text field at the bottom and hit <em>Send</em>. The AI chatbot will respond to your message.</li>
</ol>
<p>You can now engage in a conversation with your AI chatbot! Feel free to experiment with different types of messages and see how the AI responds.</p>
<h1>Final words</h1>
<p>Thank you for reading! The complete source code can be found <a href="https://github.com/jakeprins/nextjs-chatgpt-tutorial">here</a><a href="https://github.com/jakeprins/nextjs-chatgpt-tutorial.">.</a></p>
<p>If you found this tutorial useful, please connect with me on <a href="https://twitter.com/jake_prins">Twitter</a> or visit my personal website at <a href="https://jakeprins.com/">jakeprins.com</a>.</p>
<p>In addition, be sure to check out <a href="http://teamsmart.ai/">TeamSmart AI</a> for my complete ChatGPT application. It’s a Chrome extension that allows different AI team members to help you with your daily tasks.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[From Zero to Acquired: My Journey to Selling Serverless SaaS]]></title>
            <link>https://jakeprins.com/blog/zero-to-acquired</link>
            <guid>https://jakeprins.com/blog/zero-to-acquired</guid>
            <pubDate>Sun, 12 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[In this article, I share my journey from developing side-projects that barely made any money to selling a successful project on Acquire.com.]]></description>
            <content:encoded><![CDATA[<p>Back in 2019, I began listening to the <a href="https://37signals.com/podcast/">Rework podcast</a>, which is based on the business book and blogs of Jason Fried and DHH from Basecamp. The first episode, “Sell Your By-Products”, discussed how everything has by-products, such as the book and podcast Rework itself, which were created from years of blogging. <a href="http://www.rubyonrails.com/">Ruby on Rails</a>, the popular programming framework, was a by-product of building Basecamp, and Basecamp itself was initially created as an internal tool.</p>
<p>The trick is to spot these by-products and see opportunities. So, I wondered if I had created any by-products. I was working full-time and had launched multiple products I had created during nights and weekends, but none of them had made much money or gained any significant traction.</p>
<blockquote>
<p>The trick is to spot these by-products and see opportunities.</p>
</blockquote>
<p>I had created my own boilerplate of code that I re-used to bootstrap new projects. It was a simple React project, with all the packages I regularly used installed, and it included a couple of simple code generators. Could this be considered a by-product?</p>
<p>I decided to give it a try, so I bought a landing page template and set up a website. I named the project React Milkshake – it sounded cool – and used the 🥤 emoji as the logo. I also bought the <a href="http://reactmilkshake.com/">reactmilkshake.com</a> domain.</p>
<p>To make the product available, I connected my site to Gumroad. People could then buy the code of the boilerplate for $29.</p>
<p>Within a weekend, I had set up the new product and scheduled a launch on ProductHunt. This was the first time I had spent so little time on a project, and it felt really strange.</p>
<p>I launched and sold two items worth $58. The next day, I made another sale, bringing my total to $87 in two days. The sales would end there, but that was alright. I had earned more money with this weekend project than I had with the other projects I had launched that never made a profit.</p>
<h2>Improving the Product</h2>
<p>I found a project people would pay for and realized time savings were the reason. So, I decided to add features many developers need when starting new projects. I created a new version of React Milkshake, which included Firebase authentication (login, signup, reset password). I also improved the code generators, documentation, and overall look.</p>
<p>I sold this new version as React Milkshake Firebase and charged $49. It wasn&#x27;t an instant hit, but there were some people who bought it. And, with the higher price, I got better returns.</p>
<p>For my next iteration, I added a database with Firebase Firestore. I created a CLI that would allow users to generate CRUD actions with views and Firestore integration. I named it React Milkshake Serverless and built it with the new framework Next.js.</p>
<p>Sales were slow, but I achieved better returns with a higher price point ($79).</p>
<h2>Creating a SaaS Starter Kit</h2>
<p>I had the idea of creating a SaaS starter kit from scratch, with everything you need, like a landing page, CMS, authentication, dashboard, admin features, and more. This eventually became <a href="https://serverless.page">Serverless SaaS</a>.</p>
<p>I decided to do things differently this time. Instead of first building everything and then setting a launch date, I started with the landing page. <a href="https://www.indiehackers.com/product/serverless-saas">I shared updates on Indiehackers.com</a> and through the mailing list. By the time I launched, I had 200 people subscribed. The launch was a success, and I made <strong>$2,000</strong> in the first two weeks! The month after, I had my best month in terms of sales: <strong>$3,600</strong>.</p>
<p>You can read more about my journey and the things I did before the launch <a href="https://www.jakeprins.com/blog/how-i-went-from-2k-in-a-year-to-2k-in-a-week">here</a>.</p>
<h2>Selling Serverless SaaS</h2>
<p>In the first year after launch, I spent a lot of time improving the product and releasing new updates. The following year, I didn&#x27;t invest as much time. Two years after launch, I had lost most of my interest in the project and sales had decreased slightly, so I decided to put it up for sale on <a href="http://acquire.com/">Acquire.com</a>.</p>
<p>The process was simple. I created a listing on their website and provided all the details. I evaluated potential buyers and accepted a letter of intent (LOI). During due diligence, I shared evidence with the buyer to prove my numbers and other details. An asset purchase agreement (APA) was established, and <a href="http://escrow.com/">Escrow.com</a> was used to deposit the money. Finally, I transferred all assets and the buyer released the funds.</p>
<p>I was happy with the sale and the process. I had a great experience with the buyer and the team at Acquire. I can highly recommend this platform to anyone looking to sell their product.</p>
<h2>Summary</h2>
<p>In this article, I shared my journey from developing side-projects that barely made any money to selling a successful project on <a href="http://acquire.com/">Acquire.com</a>. I identified a by-product that people would pay for, iterated and improved it, and eventually sold it. I shared my journey with the trials and tribulations of building, launching, and selling a side project. I hope this article has been inspiring or useful to those who have read it. If you have any questions or comments, please don&#x27;t hesitate to reach out to me on Twitter. And please subscribe to the mailing list to stay up to date with my latest articles.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[The Importance of NFT Royalties]]></title>
            <link>https://jakeprins.com/blog/the-importance-of-nft-royalties</link>
            <guid>https://jakeprins.com/blog/the-importance-of-nft-royalties</guid>
            <pubDate>Wed, 07 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Why supporting creators is essential for the survival of the NFT Market and the important role of marketplaces in enforcing royalties.]]></description>
            <content:encoded><![CDATA[<p>NFTs and digital collectibles have exploded in popularity over the past few years, with many artists and creators using them to sell unique digital assets like art, music, and other digital media. A critical aspect of the NFT market is the use of royalties, which allow the creator of an NFT to earn a percentage of the sales price whenever their NFT is resold.</p>
<p>However, not all NFT marketplaces enforce royalties. Some marketplaces even removed royalties or made them optional. This is a concerning trend, as it undermines the ability of creators to earn a continuous return on their work and could ultimately harm the growth of the NFT market.</p>
<p>One of the main reasons that some marketplaces are removing royalties is that they are scared to lose market share and desperately try to make their platforms more attractive to buyers. Without the added cost of royalties, buyers may presumably be more likely to purchase NFTs on these platforms. However, this approach needs to pay attention to the fact that creators are driving the popularity of NFTs.</p>
<p>Gifted artists should be able to create and share their work and be incentivized to do that in a way that allows them to thrive. NFT marketplaces must enforce royalties to ensure that creators are fairly compensated for their work. Without the ability to earn royalties, creators are less likely to create and sell their work using these technologies, ultimately harming the growth of the NFT market.</p>
<blockquote>
<p>99.9% of all the artists disappear. That’s what happens. — Tim Ferris</p>
</blockquote>
<p>Enforcing royalties helps to support the overall health of the space by ensuring that creators have a financial incentive to continue creating and supporting their work. NFT projects need fuel to make them run. It doesn’t matter if it’s from a single artist or a company of people with a roadmap who wants to deliver to their community. In either case, there needs to be fuel.</p>
<h2>Marketplaces that Neglect Royalties</h2>
<p>Marketplaces like <a href="https://sudoswap.xyz/#/">sudoswap</a> and <a href="https://x2y2.io/">X2Y2</a> are popular among traders specifically because they’re royalty-free. Existing NFT marketplaces have felt pressure to adjust so they won’t lose market share, despite the potentially catastrophic consequences for creators and the NFT market overall.</p>
<p><a href="https://magiceden.io/">Magic Eden</a>, the popular NFT marketplace on Solana, recently raised over 150 million dollars. While this influx of capital has its benefits, it also has drawbacks. To succeed, Magic Eden needs to grow at all costs and do everything to remain the most prominent platform on Solana. To maintain its leading position on Solana, Magic Eden decided to <a href="https://nftnow.com/features/magic-eden-makes-creator-royalties-optional/">make royalties optional</a>, despite anticipating backlash from the community. This decision has been criticized by many in the NFT community, who believe that enforcing royalties is essential for supporting creators and ensuring the growth of the NFT market.</p>
<p><a href="https://looksrare.org/">LooksRare</a>, an Ethereum NFT marketplace, recently made it optional for buyers to pay creator royalties when trading NFTs. Despite the <a href="https://decrypt.co/112333/ethereum-nft-market-more-serious-solana-fidenza-artist-tyler-hobbs">pushback</a> from many NFT artists and creators against the <em>race to the bottom</em> of rejecting royalties, LooksRare chose to let buyers opt out of paying royalties. Although they claimed to still “support creators” by sharing protocol fees, this meant that creators only received a 0.5% share of the sales price on secondary sales, paid out in the LOOKS token.</p>
<h2>Royalties: Here to Stay?</h2>
<p>While we are seeing many NFT marketplaces move to 0% royalties, it doesn’t mean that royalties will be a thing of the past. Many people from the industry have spoken up against these developments. The NFT community seemed to understand that royalties play an essential role in the development of the space. Prominent artists and creators are making their views known.</p>
<blockquote>
<p>The NFT ecosystem would be a tiny fraction of what it is today if it weren’t for creator royalties. — Wylie Aronow, Co-Founder of <a href="https://medium.com/u/be0a4e30f9c?source=post_page-----f836e1b5840--------------------------------">Yuga Labs</a></p>
</blockquote>
<p>These noises seem to affect how NFT marketplaces look at the situation. Top marketplace <a href="https://decrypt.co/113698/opensea-breaks-silence-on-nft-royalties-but-creators-dont-like-what-they-hear">OpenSea was saying</a> that it may follow the trend of no longer enforcing creator royalties on secondary sales. Still, after the backlash of these statements, it decided to shift.</p>
<h2>Positive Developments in the Royalties Discussion:</h2>
<ul>
<li>The new platform <a href="https://universal.page/">Universal Page</a> proposed a <a href="https://github.com/lukso-network/LIPs/pull/141">standard for NFT 2.0 royalties</a>, allowing creators to enforce royalties only when people sell and profit from their work but not take royalties when they sell at a loss. On <a href="https://universal.page/">universal.page</a> creators can also set multiple receiving addresses with different percentages or fixed amounts.</li>
<li>OpenSea launched a <a href="https://opensea.io/blog/announcements/on-creator-fees/">tool for on-chain enforcement</a> of creator fees and decided to continue enforcing creator fees for collections.</li>
<li>Magic Eden created an open-source <a href="https://blog.magiceden.io/magiceden-open-creator-protocol">Open Creator Protocol</a> to protect royalties. New collections can launch with this protocol to protect royalties.</li>
<li>Many NFT marketplaces, like Rarible, Nifty Gateway, MakersPlace, and more, created the <a href="https://royaltyregistry.xyz/lookup">Royalty Registry</a>: a smart contract that enables creators to apply on-chain royalty enforcement to their work.</li>
</ul>
<h2>Conclusion</h2>
<p>The importance of NFT royalties cannot be overstated. Without the ability to earn royalties, it’s unlikely that creators will continue to create and sell their work using blockchains, ultimately harming the growth of the space. There should be clear royalty standards, and every NFT marketplace must enforce royalties to support the creators who are driving the growth of this exciting new technology.</p>
<p>The removal of NFT royalties by some marketplaces has caused concern among creators. However, the recent developments in the NFT royalties discussion show that there is hope for a brighter future. Many marketplaces are listening to the community and implementing new protocols and standards to protect creators and enforce royalties. This demonstrates that a significant part of the NFT industry is committed to supporting creators and ensuring fair compensation for their work.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[What are NFTs 2.0, and which problems do they solve?]]></title>
            <link>https://jakeprins.com/blog/what-are-nfts-2-0-and-which-problems-do-they-solve</link>
            <guid>https://jakeprins.com/blog/what-are-nfts-2-0-and-which-problems-do-they-solve</guid>
            <pubDate>Wed, 28 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[To unlock the full potential of NFTs, we need smart contracts that allow more flexibility.]]></description>
            <content:encoded><![CDATA[<p>NFTs today have limited functionality and interoperability.
They have fantastic potential but are primarily used for speculation these days. To unlock the full potential of NFTs, we need smart contracts that allow more flexibility.</p>
<h2>Current standards are too limited</h2>
<p>Most NFTs are based on the <a href="https://docs.openzeppelin.com/contracts/3.x/erc721#:~:text=ERC721%20is%20a%20standard%20for,across%20a%20number%20of%20contracts.">ERC-721</a> standard, which makes it easy to exchange “non-fungible” tokens. Launched in 2017, the standard was used by <a href="https://www.cryptokitties.co/">CryptoKitties</a>, a popular online game that was one of the first big projects that showed the potential of NFTs.</p>
<p>In essence, contracts using this simple standard include a base URI and incremental IDs representing individual tokens. Add the tokenID to the base URI, and you should have a URL that can be used to fetch the token’s metadata.</p>
<p>This allowed creators to build collections of multiple unique NFTs. The metadata is often stored on <a href="https://ipfs.io/">IPFS</a>, a distributed storage network, but plenty of NFT projects just use a centralized service like Google Drive. Buyers of NFTs often don’t realize that if the creators delete or move the files, their NFT is nothing more than a pointer to an unknown location.</p>
<p>But this is in itself not the biggest problem with this standard. There is currently no way to validate if an NFT project is solid.</p>
<p>One of the most critical aspects of art is the creator, but nothing about the creators is being stored on-chain.</p>
<p><strong>The only data attached to an ERC-721 contract is a <code>name</code> and <code>symbol</code>.</strong></p>
<p>The collection pages on platforms like <a href="https://opensea.io/">OpenSea</a> seem rich with information, but most of this data is not coming from the smart contract of the NFT. It’s something that has been added through an input field on the platform itself, meaning it lives inside the database of that specific marketplace. NFT collections could potentially have very different information shown on other websites.</p>
<p>If your favorite marketplace no longer exists five years from now, all your NFT represents is a name, symbol, and ID.</p>
<p><strong>Most importantly, your NFT says absolutely nothing about its creators.</strong> Although the smart contract has the owner’s public key, this key could belong to anybody and holds no information about the original creators.</p>
<p>The creators of an NFT, and their reputation, are an essential aspect of the value of an NFT or any kind of art. Especially when more professional designers and creators use NFTs, think of digital fashion and big brands working with well-known designers. Don’t you want your NFT to hold some information about them?</p>
<p>Would most paintings be as worthy if no one knew who painted them? Would you even trust if it’s real or just a scam?</p>
<p>Being able to buy directly from an artist with an on-chain reputation would drastically reduce the number of scams. But with current standards, all this data does not exist.</p>
<p>Most of the creators&#x27; profiles now live on centralized marketplaces and could vanish over time. Current platforms have evolved way faster than the underlying standards. The standards are lacking behind and are holding the industry back.</p>
<p>For NFTs to grow far beyond pointers to a file of a monkey picture, we might need to take a giant leap to use new standards built from the ground up and designed for the future of Web3.</p>
<h2>Introducing NFTs 2.0</h2>
<p>The creator of the ERC-20 standard, <a href="https://twitter.com/feindura">Fabian Vogelsteller</a>, has developed new standards to take web3 to the next level. He started a new blockchain, <a href="https://lukso.network/">LUKSO</a>, and proposed <a href="https://docs.lukso.tech/standards/nft-2.0/LSP7-Digital-Asset">LSP7 Digital Asset</a> and <a href="https://docs.lukso.tech/standards/nft-2.0/LSP8-Identifiable-Digital-Asset">LSP8 Identifiable Digital Asset</a>, also referred to as <strong>NFT 2.0</strong>.</p>
<p>With these, NFT and token metadata inputs are very extensible. The on-chain metadata inputs could be changed, allowing NFTs to evolve over time.
The contract can have multiple creators attached to it, and instead of just some public keys, these are addresses of <a href="https://docs.lukso.tech/standards/universal-profile/introduction">Universal Profiles</a>. These profiles show the face and the reputation of the original creators.
The NFTs can also inform receivers of assets, making complex smart contract automation possible.</p>
<p>LSP7 and LSP8 are using the <a href="https://github.com/ERC725Alliance/ERC725/blob/main/docs/ERC-725.md#erc725y">ERC725Y</a> standard as their base. By using a generic data key-value store, a contract now comes with much-needed features:</p>
<ul>
<li>Attaching an unlimited amount of information makes the asset more customizable and unique.</li>
<li>Knowing when the metadata has been altered</li>
<li>Having metadata as a hash reference instead of a URL reference</li>
</ul>
<p>This base will allow more customization and upgradability of the metadata over time. Summarized, these standards will be:</p>
<ul>
<li><strong>Authentic &amp; Verifiable</strong>
Smart contracts can now verify whether a specific Universal Profile created an NFT.</li>
<li><strong>Flexible &amp; Updatable</strong>
Metadata can be updated, enabling dynamic NFTs to change certain things over time.</li>
<li><strong>Secure metadata</strong>
You can now add unlimited secure metadata that can also be smart contract readable.</li>
<li><strong>Safer</strong>
NFTS can only be sent to Universal Profiles or a smart contract that can work with them, preventing the lock of NFTs.</li>
</ul>
<h2>Summary</h2>
<p>The current standards representing NFTS are limiting the industry as they lack standardization and many powering features.</p>
<p>ERC-721 is the most used NFT standard but is not much more than a representation of incremental tokenIDs without proper metadata or asset representation. It has no standard interaction between sender and receivers and no security measurements to ensure safe asset transfers.</p>
<p>NFT 2.0 is a collective name for the new token and NFT standards <a href="https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-7-DigitalAsset.md">LSP7-DigitalAsset</a> and <a href="https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-8-IdentifiableDigitalAsset.md">LSP8-IdentifiableDigitalAsset</a>.</p>
<p>They have been designed from the ground up and have new standards for flexible metadata, more secure transfers, more asset representation, and more interactive transfer.</p>
<p>Combining NFTs 2.0 with the use of <a href="https://docs.lukso.tech/standards/universal-profile/introduction">Universal Profiles</a>, we could now have a way to allow creators to build on-chain reputations.</p>
<p>NFTs 2.0 could bring the much-needed industry&#x27;s much-needed innovation if it ever wants to evolve from trading profile pictures for pure speculation.</p>
<h2>Mint your first NFT 2.0 for free</h2>
<p>When you start with <a href="https://universal.page/">UniversalPage</a>, you can mint a free NFT that will hold your <a href="https://universal.page/blog/universal-page-name"><strong>UniversalPageName</strong></a>. This name will serve as an identifier for your personal website, meaning your page will be created under the subdomain: <em>your-name</em>.universal.page.</p>
<p>You can use UniversalPage to create your personalized website to showcase and sell digital assets. Or discover and collect new NFTs on the open marketplace.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Why smart contract-based accounts will drive further adoption of Web3]]></title>
            <link>https://jakeprins.com/blog/why-smart-contract-based-account-drive-further-adoption-of-web-3</link>
            <guid>https://jakeprins.com/blog/why-smart-contract-based-account-drive-further-adoption-of-web-3</guid>
            <pubDate>Fri, 03 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Let’s explore how smart contract-based accounts can help solve the main problems of Web3.]]></description>
            <content:encoded><![CDATA[<h2>Improving the Onboarding and User Experience.</h2>
<p><img src="https://miro.medium.com/max/1400/1*sh5Y4L04qsTDyct1uRgGQA.png" alt=""/></p>
<p>The user experience of web apps has improved drastically in the last 5–10 years. Everybody uses the web to quickly book flights, make reservations, send money, schedule meetings, read the news, connect with friends, etc.</p>
<p>Internet companies do everything to make their web applications as fast and user-friendly as possible. You only need an internet connection to get started.</p>
<p>Now think of the web3 applications and summarize the steps you must take before starting. And what can go wrong along the way:</p>
<ul>
<li><strong>Download a browser extension</strong>
Most people start with installing the <a href="https://metamask.io/">Metamask</a> browser extension.</li>
<li><strong>Create a wallet</strong>
Write down 12 words that you need to keep safe. “Lose your keys, and you lose your funds” - a scary message for newcomers.</li>
<li><strong>Sign up on a crypto exchange</strong>
This usually requires <a href="https://www.thalesgroup.com/en/markets/digital-identity-and-security/banking-payment/issuance/id-verification/know-your-customer">KYC verification</a>, which could take a couple of days to complete. Not something you want to do if you just want to try out web3. You have to be highly motivated to go through this process.</li>
<li><strong>Deposit money</strong>
If everything goes well, you can now deposit some money into your account. If you don’t have a credit card, you might need a wire transfer, which could take a couple of days.</li>
<li><strong>Buy tokens</strong>
Many days later, you can finally buy some ether (or another native token) needed to cover the transaction fees (gas) for interacting with the blockchain.</li>
<li><strong>Send tokens to your wallet</strong>
Now you need to send that ether to your wallet. This can go wrong; if it does, you might not be willing to give web3 another try anytime soon. You probably also get charged for that transaction, and with high gas fees, this could mean you end up with much less ether in your wallet than you anticipated.</li>
<li><strong>Make a transaction</strong>
Finally, after your ether has arrived in your wallet, you can try to do a transaction. Maybe you want to buy an NFT, but you don’t have enough ether to cover the gas fees because you didn’t expect the transaction fees to be that high. Now you need to go back to the crypto exchange, buy more ether, etc.</li>
</ul>
<p>A lot of effort must be spent before making your first transaction on a blockchain. Although crypto adoption seems to grow massively, it’s mostly still the early-adopter and tech-savvy people willing to go through this process.</p>
<h2>Adoption vs. Speculation</h2>
<p>Although it’s estimated that almost <a href="https://www.insiderintelligence.com/insights/us-adults-cryptocurrency-ownership-stats">1 of 5 adults in the US owns crypto</a>, the number of people who have transacted with their own crypto wallet is likely only a fraction. A big group of crypto buyers is only involved in speculation and never gets their crypto off exchanges.</p>
<p>The benefits must be clear for web3 to get more widely adopted, and the initial onboarding needs to become way more effortless.</p>
<p>The steps you must go through to make your first interaction with a decentralized application are just too much of a hassle for the regular person. Every action the user needs to take increases the change they give up.</p>
<p>People expect fast and easy-to-use applications, and there is no reason to make an exception for applications powered by a blockchain.</p>
<h2>Improving UX with Blockchain Accounts</h2>
<p>We can not continue with the industry&#x27;s current standards to solve the onboarding problem.</p>
<p>The user experience needs to be better for wider adoption ever to happen.</p>
<p><a href="https://twitter.com/feindura">Fabian Vogelsteller</a>, once a lead developer at the Ethereum foundation and the creator of the ERC-20 standard, has been working on solutions for the most important problems of the web3 experience. He started <a href="https://lukso.network/">LUKSO</a>, a new EVM chain much like Ethereum 2.0, and with his team, developed new standards that could help the industry forward. Potentially the most impactful one is <a href="https://docs.lukso.tech/standards/universal-profile/introduction">Universal Profiles</a>: smart contract-based accounts.</p>
<p>A Universal Profile (often called UP) can serve as your wallet and the public profile you use to interact with decentralized apps. They provide the flexibility needed to create better user experiences that key-based wallets like Metamask do not.</p>
<p>A Universal Profile can:</p>
<ul>
<li>hold funds (crypto/NFTs)</li>
<li>interact with any address or contract</li>
<li>have any information attached</li>
</ul>
<p>Although the profile can be created like any other web profile, much more is under the hood. A Universal Profile contains three smart contracts that work together:</p>
<ul>
<li>The <a href="https://docs.lukso.tech/standards/universal-profile/lsp0-erc725account"><strong>ERC725Account</strong></a> is a smart contract that contains some specific ERC725Y keys that describe the profile. This standard represents the core account contract, allowing the profile to interact with any address or smart contract on the blockchain.</li>
<li>The <a href="https://docs.lukso.tech/standards/universal-profile/lsp6-key-manager/"><strong>Key Manager</strong></a> is a smart contract attached to an ERC725Account and can act as the profile owner. Permissions for an address are stored inside the key-value store of the ERC725Account contract, and the Key Manager can read those permissions and perform a transaction or restrict access.
It also allows relay transactions, enabling third parties to pay a user’s transaction fees (GAS).
This can solve the issue of losing all your funds when you lose your keys. You can now have a backup account that you can give specific permissions.
You can also permit platforms to relay a transaction for you, meaning they can cover the gas fees. If a platform makes money, with a subscription model, for example, it can easily cover the gas fees for its users. This solves the issue of first needing to go to a centralized exchange. LUKSO will subsidize early adopters by running a public relay service, allowing anyone to interact with the LUKSO blockchain without complexity.</li>
<li><a href="https://docs.lukso.tech/standards/universal-profile/lsp1-universal-receiver-delegate/"><strong>Universal Receiver Delegate</strong></a>. This contract knows incoming transactions and handles those incoming value transfers by registering the data keys representing them. Your Universal Profile will be updated directly when it receives new tokens or NFTs, so you are always aware of what tokens you own. It can execute any custom logic. For example:
- reverse all receiving tokens
- reverse only the tokens that are not verified
- automatically selling all incoming tokens directly through a decentralized exchange</li>
</ul>
<p><img src="https://miro.medium.com/max/1400/1*ifoo3t9ObHOBdphEgTUmFQ.jpeg" alt=""/>LSP0 ERC725 Account overview (from the <a href="https://docs.lukso.tech/standards/universal-profile/lsp0-erc725account">LUKSO documentation</a>)</p>
<h2>A platform-independent profile</h2>
<p>A Universal Profile is an open standard and operates platform-independent. Consider current web2 accounts&#x27; limitations, like your Twitter profile. Going to a different social platform means you must build your following and reputation from scratch. You can not take all your Twitter data with you. It’s also not your data. You don’t own that account, you are renting it. The data lives inside the company&#x27;s database and is controlled by them.</p>
<p>With Universal profiles, you own your data, and you can give platforms permission to access (parts of) it. If you don’t like the platform anymore, you can go to a competitor and take all your data with you. This puts users back in control and improves the user experience. With the click of a button, you can connect your profile to an app, and all your data is right there.</p>
<h2>Summary</h2>
<p>Web3 can give great benefits to people on the internet. From interacting with defi-protocols to gaining ownership over digital items to decentralized social networks that give back control and ownership to creators. It can improve the user experience because people only need to connect their wallet/profile and have all their data and belongings available.</p>
<p>But for web3 to get further adoption and see more people use blockchain applications (instead of only speculating on the price of tokens), we need a more frictionless onboarding experience and easy-to-use apps that demonstrate the benefits for the users.</p>
<p>There are solutions in the making, and <a href="https://docs.lukso.tech/standards/universal-profile/introduction">Universal Profiles</a> is one of them that has a lot of potentials. Initially available on Lukso, a new blockchain with a set of tools and standards for developers to take the current state of web3 to the next level.</p>
<p>Although you still need to download a browser extension, everything after that is way better than the current state of crypto wallets.</p>
<p>You can already experience the ease of creating a UP by <a href="https://docs.lukso.tech/guides/universal-profile/browser-extension/install-browser-extension">downloading the browser extension</a> and creating your profile.</p>
<p>You can mint your first NFT for free on <a href="https://universal.page/"><strong>Universal Page</strong></a>, the first NFT marketplace on <a href="https://www.lukso.network/">LUKSO</a>, which also allows you to turn your Universal Profile into a customizable website where you can showcase and sell digital assets.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How I went from $2k in a year to $2k in a week.]]></title>
            <link>https://jakeprins.com/blog/how-i-went-from-2k-in-a-year-to-2k-in-a-week</link>
            <guid>https://jakeprins.com/blog/how-i-went-from-2k-in-a-year-to-2k-in-a-week</guid>
            <pubDate>Tue, 24 Nov 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[After many hours of work, it was finally time to give people access to my new project, a SaaS boilerplate. In the first 5 days of early access, I almost made $2k in sales. Read about what I did and the results.]]></description>
            <content:encoded><![CDATA[<p>After many hours of work, it was finally time to give people access to my new project, a SaaS boilerplate. In the first 5 days of early access, I almost made $2k in sales ($1,901.40 to be exact).</p>
<p>I have made and sold a React boilerplate before, <a href="https://www.reactmilkshake.com/">React Milkshake</a>. In the week after launching React Milkshake I made just 29 dollars. I, later on, created upgraded versions of the boilerplate. Every new version sold a bit better than the previous one, but with my completely new project <a href="https://serverless.page/">Serverless SaaS</a> I have made more in one week than with all the previous projects combined in a year.</p>
<p>This didn’t just happen because I just got luckier this time. Here is what I did differently.</p>
<h3>Landing page before product building</h3>
<p>The first mistake I made in the previous projects was working by myself until it was time to launch. This meant I didn’t have an audience.</p>
<p>Inspired by the #BuildInPublic movement, I decided for my new project to first build <a href="https://serverless.page">the landing page</a> and open up a mailing list so people could subscribe and follow the progress of me building the product. Every now and then I shared some updates and slowly grew the mailing list to over 100 people that were sincerely interested in what I was making.</p>
<h3>Blogging</h3>
<p>In the past, I had written some articles on Medium before, but I decided to at least write one or two new blog posts every month. In total, I have at least 10 new blog posts that are related to some of the technologies that are being used in <a href="https://serverless.page/">Serverless SaaS</a>. Most of them are tutorials like <a href="https://medium.com/better-programming/how-to-set-up-next-js-with-tailwind-css-b93ccd2d4164">How to setup Next.js with Tailwind</a> or <a href="https://medium.com/better-programming/how-to-implement-netlify-cms-with-next-js-4b8721bdec45">How to implement Netlify CMS with Next.js</a>, but also a series called <em>stack choices</em> in which I compare different frameworks or technologies with each other as <a href="https://codeburst.io/stack-choices-react-vs-vue-vs-angular-vs-svelte-49aa0170c634">Angular vs React vs Vue vs Svelte</a>.</p>
<p>Writing these blog posts had multiple purposes and ended up with benefits:</p>
<ul>
<li>Learned a lot about these subjects</li>
<li>Provided value for other people learning about these subject</li>
<li>Earned some money with the <a href="https://help.medium.com/hc/en-us/articles/115011694187-Getting-started-with-the-Medium-Partner-Program#h_01EECWD3WWHZTJMF5PTK7AAM2C">Medium Partner Program</a></li>
<li>Gave me the ability to drop a link to my new project</li>
<li>Gave me the ability to drop a link to my personal site and Twitter</li>
<li>Increased my followings on Medium</li>
</ul>
<p>The people who read my blog posts could also be future customers because the starter-kit is mainly for developers (or people who work with developers).</p>
<h3>Mailing list</h3>
<p>This time I had built up a mailing list with around 100 subscribers, all with people who were interested in the boilerplate. Around 50% opened the emails I had to send and around 30% clicked the links to the site. This list is still growing because I still allow people to sign up to get regular updates.</p>
<p>Besides that, I already had a personal mailing list of people who had signed up for previous products I have to build like <a href="https://codestash.co/">codestash</a>, <a href="https://www.makermove.com/">makermove</a>, and <a href="https://www.raterfox.com/">raterfox</a>.</p>
<p>Because I started blogging more I updated <a href="https://jakeprins.com/">my personal site</a> and also added a signup field for this personal mailing list to stay informed about blog posts or product updates. In total, I had around 1000 people on this list to send out an announcement, but just 20% of that list opened the email and only 2.5% clicked the link. Those numbers are not great, but most of these subscribers come from <a href="http://raterfox.com">raterfox.com</a>, a social platform for entertainment, which is clearly not my target audience.</p>
<h3>Twitter</h3>
<p>Twitter is a great platform for talking in public about the process and updates on your products. I wasn’t very active on Twitter and mainly used it to stay informed about tech-related stuff. I decided to be more active and did manage to gain some more Twitter followers. I think the slow growth is caused mostly by being more active on not just Twitter, but also on Indie Hackers and mentioning <a href="https://twitter.com/jake_prins">my handle</a> in blog posts.</p>
<p>With the current 581 followers, I do not believe this had a very big impact on my launch. Twitter seems great for people with a couple of thousands of followers, but with &lt; 1k followers it sometimes feels like you are talking to a black hole of nothing.</p>
<p>I still think it’s a great way to share your work and be reachable by others. Some people started to DM me with questions about the boilerplate. This was already a good sign. Also, people told me they were excited about the upcoming launch, even better! Besides that, someone reached out to say my guides online were really helpful, which is always great to hear.</p>
<h3>Building a better product</h3>
<p>The first boilerplate I had build, <a href="https://www.reactmilkshake.com/">React Milkshake</a>, was a bit of an experiment. I was using it myself and wasn’t sure if other people were going to pay for it. It was very basic and doesn’t have a lot of features when it launched, but the fact that people started to buy it was super exciting for me. It proved that people are willing to pay money for a starter-kit that helped them save time.</p>
<p>For my next project, <a href="https://serverless.page/">Serverless SaaS</a>, I decided to spend more time on it and take it to the next level. I had some proof of the market, but the product needed to provide more value.</p>
<p>Most Indiehackers and developers I met online were building SaaS apps. I also had some ideas for building a SaaS, so a boilerplate that could help me build new SaaS apps faster was very helpful. If the boilerplate wouldn’t sell, I could still use it myself. So I decided to implement multiple SaaS features, like a billing integration with Stripe, and market the product as a way to build SaaS apps faster.</p>
<p>This would be more aligned with the need for most of my target audience and also allows me to ask for a higher price. It provides much more value than my other boilerplates and people are also more willing to pay for products that help them save time or make money. This project could potentially do both.</p>
<p>Instead of rushing to market and going with a full-on MVP approach, I figured I should take my time and craft a product to be proud of. After that, I could soft launch it as “Early Access”, so I could ask my first customers for feedback and improve the product while it’s being used by actual paying customers. In this soft launch period, I made more money than I did in the last year of my old project, so I guess I’m doing something right.</p>
<h2>Conclusion</h2>
<p>After years of indie hacking, I have learned a lot of valuable lessons. Looking back at the months leading to the “soft” launch of my new product, I can tell that certain activities will highly increase your chances of a successful launch.</p>
<p>Building in public, by sharing your progress and thoughts on social platforms, could definitely help a lot.</p>
<p>Taking time to write articles and provide value to others helps you in building an audience and to connect with people that might end up being a customer.</p>
<p>Also, don’t rush the process of building a product. It can be helpful to launch fast and validate your idea as quickly as possible, but if you want to provide real value that could mean you need to put in some extra time. Once you have seen some proof of evidence that people are willing to pay for your product I think it’s good to not rush your project. Don’t put too much pressure on yourself. But, when you think that MVP is ready, just ship it.</p>
<p>Thanks for reading! You can find me on Twitter (<a href="https://twitter.com/jake_prins">@jake_prins</a>) or read more at <a href="https://jakeprins.com/blog">jakeprins.com/blog</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[7 Boilerplates for a Head Start When Building a SaaS App]]></title>
            <link>https://jakeprins.com/blog/7-boilerplates-for-a-head-start-when-building-a-saas-app</link>
            <guid>https://jakeprins.com/blog/7-boilerplates-for-a-head-start-when-building-a-saas-app</guid>
            <pubDate>Tue, 17 Nov 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Building a SaaS platform is hard, especially if you’re a solo founder who needs to build everything by yourself. But there are options to help you build and launch your project faster.]]></description>
            <content:encoded><![CDATA[<p>Building a SaaS platform is hard, especially if you’re a solo founder who needs to build everything by yourself. But there are options to help you build and launch your project faster.</p>
<hr/>
<p>When you have a great idea and start working on it from scratch, you feel a rush of excitement. It’s great to work on an interesting idea, but instead of focusing on what makes your product unique, you first need to put in the hours to work on things that are less exciting: configuring a project, building authentication flows, integrating payments, building forms, etc.</p>
<p>If you want to launch faster, there are some things you might want to consider. You probably want to use a web framework to increase development speed. Nobody builds their SaaS with plain Ruby code. You’d much rather use a framework like Rails. And the same goes for styling your application. Instead of writing all the CSS yourself, you could use a framework like Bootstrap, Material, or Tailwind. But if you really want to save a lot of time, you should start with a SaaS boilerplate.</p>
<p>A SaaS boilerplate includes a lot of functionalities that cost a lot of time to build out yourself. Think about authentication or billing. These are things that every SaaS needs to have, so why build it all yourself?</p>
<p>You should launch your SaaS fast so you can get feedback as soon as possible. To do that, starting out with a boilerplate/starter kit can save you tons of time. Here is a list of seven boilerplates that could help you build your SaaS faster.</p>
<hr/>
<h3>Serverless SaaS</h3>
<p>The <a href="https://serverless.page/">Serverless SaaS</a> boilerplate is the perfect starting point for your next React app. It’s built with TypeScript, Next.js, Tailwind, and Firebase. You can tap into user authentication, payments with Stripe, teams, and more with zero effort.</p>
<p>Going serverless is a great way of cutting costs because the pay-as-you-go pricing model means you can start free and only pay when your startup gets real traction. You also don’t have to worry about scaling issues. With Serverless platforms you are outsourcing a lot of responsibilities which allows you to move quicker.</p>
<p>That makes this starter kit great for solo developers and small teams who want to launch something quickly without spending money.</p>
<img alt="Serverless SaaS logo and slogan" src="https://miro.medium.com/max/700/1*7yHuIcpwIcUBkecK2LORMg.jpeg"/>
<p>Source: <a href="https://serverless.page/">https://serverless.page/</a></p>
<hr/>
<h3>Nextless.js</h3>
<p>Nextless.js is a <a href="https://nextlessjs.com">React SaaS Boilerplate</a> that helps developers build scalable and production-ready SaaS products. It is a full-stack solution that includes all the essential features such as Authentication, Stripe Payment, Team Collaboration and Multi-tenancy, Dashboard, Landing pages, Emails, and more. The boilerplate is highly customizable and extensible using a layered architecture.</p>
<p>The entire codebase is written in TypeScript. The frontend is built with Next.js, React, and Tailwind CSS, while the backend is a Serverless Rest API. Additionally, the full-stack application can be easily deployed to AWS. It was created by a developer for developers to provide the best possible developer experience. Therefore, it comes with complete documentation, and email support to help you in the journey to build your SaaS product.</p>
<p><code>Nextless.js</code> is the ideal boilerplate to launch your SaaS. With Nextless.js, you can create and deploy your SaaS product with ease, allowing you to focus on your business while saving time on development.</p>
<img alt="Nextless" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vo5ugH6I--/c_imagga_scale,f_auto,fl_progressive,h_900,q_auto,w_1600/https://creativedesignsguru.com/assets/images/themes/next-js-saas-starter-kit.jpg"/>
<p>Source: <a href="https://nextlessjs.com">https://nextlessjs.com</a></p>
<h3>Jumpstart</h3>
<p><a href="https://jumpstartrails.com/">Jumpstart</a> is a great starter kit for your next Ruby on Rails app. You can skip the boilerplate set up and build your Rails app faster. It handles all the stuff you need like a user authentication system and even background processing. It also has very good <a href="https://jumpstartrails.com/docs">documentation</a>. You can even try out a <em>lite</em> version that is available for free on <a href="https://github.com/excid3/jumpstart">Github</a>.</p>
<img alt="Jumpstart home page" src="https://miro.medium.com/max/700/1*8vcaD5txgZRmwqHrxMPvUQ.png"/>
<p>Source: <a href="https://jumpstartrails.com/docs">https://jumpstartrails.com</a></p>
<hr/>
<h3>Laravel Spark</h3>
<p><a href="https://spark.laravel.com/">Laravel Spark</a> is a Laravel package that is built by Taylor Otwell, the creator of Laravel. An amazing project to help you build your next great product with PHP. It provides a lot of scaffolding features so you don’t have to code everything yourself. It also has the features that every SaaS business needs, like (two-factor) authentication, subscription billing, and invoices.</p>
<img alt="Spark logo" src="https://miro.medium.com/max/700/1*OIiJ6wMYQE3XFx48b7NWvw.png"/>
<p>Source: <a href="https://spark.laravel.com/">https://spark.laravel.com/</a></p>
<hr/>
<h3>Gravity</h3>
<p>With Gravity, you can build a Node.js and React SaaS app at warp speed. It comes with a lot of features and pre-built components to get you up and running quickly. It even has a built-in user-onboarding flow feedback widget.</p>
<img alt="Gravity home page" src="https://miro.medium.com/max/700/1*2R71R0rWY_9N8I2wnrAfVA.png"/>
<p>Source: <a href="https://usegravity.app">https://usegravity.app</a></p>
<hr/>
<h3>Sjabloon</h3>
<p><a href="https://www.getsjabloon.com/">Sjabloon</a> is a modern Ruby on Rails SaaS starter kit that also includes all the SaaS features like authentication and payments with Stripe, but also comes with a huge UI components library that is built with Tailwind, the rising CSS framework. This allows you to focus on your core product right from the start. Just like Jumpstart, it also has a lite version that is available for free on <a href="https://github.com/GetSjabloon/sjabloon-lite">Github</a>.</p>
<img alt="Sjabloon home page" src="https://miro.medium.com/max/700/1*cDQ56JMFLiPsqdiDPmjlkg.png"/>
<p>Source: <a href="https://www.getsjabloon.com/">https://www.getsjabloon.com/</a></p>
<hr/>
<h3>Wave</h3>
<p>Voyager is a popular Laravel Admin Package and <a href="https://wave.devdojo.com/">Wave</a> is a starter kit built on top of both Laravel and <a href="https://voyager.devdojo.com/">Voyager</a>. You can install this starter-kit on your own server and customize it to fit your needs. Wave integrates with both Stripe or Braintree and is well documented. It also has theme support for three different starter themes. You can choose from Bootstrap, UIKit, or Tailwind. Wave also checks all the boxes for all the other SaaS features so it will much likely speed up your development time when crafting a SaaS from scratch.</p>
<img alt="Wave home page" src="https://miro.medium.com/max/700/1*1dYotspAZBJ9xXBFGIaLSQ.png"/>
<p>Source: <a href="https://wave.devdojo.com">https://wave.devdojo.com</a></p>
<hr/>
<p>Thanks for reading! I hope you find these helpful.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Implement Netlify CMS With Next.js]]></title>
            <link>https://jakeprins.com/blog/how-to-implement-netlify-cms-with-next-js</link>
            <guid>https://jakeprins.com/blog/how-to-implement-netlify-cms-with-next-js</guid>
            <pubDate>Mon, 19 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[If you’re starting a new React project, you might want to consider Next.js and Netlify CMS. In this article, we take a look at why this would be a great choice and walk through the process of setting up a new project.]]></description>
            <content:encoded><![CDATA[<h4><em>Manage the content of your Next.js apps through a free Git-based CMS</em></h4>
<p>If you’re starting a new React project, you might want to consider <a href="https://nextjs.org/">Next.js</a> and <a href="https://www.netlifycms.org/">Netlify CMS</a>. In this article, we take a look at why this would be a great choice and walk through the process of setting up a new project using these technologies.</p>
<h2>Why Next.js?</h2>
<p>As I stated in <a href="https://medium.com/better-programming/how-to-set-up-next-js-with-typescript-5f912766c88">a previous article</a>:</p>
<blockquote>
<p>“There are many important details you need to consider when you start a new project with React. Your code has to be bundled using a bundler like webpack and transformed using a compiler like Babel.</p>
<p><a href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a> can be a nice tool to handle this for you and give you a massive head start, but what about code-splitting, pre-rendering for performance, and SEO or server-side rendering?</p>
<p>To build a complete React application, you need more than CRA provides you with. You can save yourself some time by using Next.js, a React framework that provides a solution to all of these problems.”</p>
</blockquote>
<p>If you want to read more about CRA vs. Next.js, check out <a href="https://codeburst.io/stack-choices-create-react-app-vs-next-js-f0f3c4db7083">Stack choices: Create React App vs Next.js</a>.</p>
<h2>Why Netlify CMS?</h2>
<p><a href="https://www.netlifycms.org/">Netlify CMS</a> is an open-source Git-based content management system. It is based on client-side JavaScript and handles content updates directly in Git. Because all content is just stored in your Git repository, you don’t need to have anything hosted on a server. It’s completely free and a great fit to combine with Next.js to build landing pages or blogs that are manageable through a nice UI.</p>
<h2>Let’s Begin</h2>
<p>Let’s begin building our landing page with some basic text to get familiar with Netlify CMS. You can find the source code of this part of the tutorial <a href="https://github.com/jakeprins/nextjs-netlify-cms">on GitHub</a> or check out <a href="http://serverless.page/">serverless.page</a> for a complete boilerplate that also includes features like authentication and billing.</p>
<h3>Set up your project</h3>
<p>You can start by creating a new Next.js project with <code>npx create-next-app</code>. In this example, we are going to use TypeScript. You can follow <a href="https://medium.com/better-programming/how-to-set-up-next-js-with-typescript-5f912766c88">this tutorial</a> on how to set up Next.js with Typescript.</p>
<h2>Using Markdown Files for Our Content</h2>
<p>Netlify CMS works with markdown files, and it’s a great way to manage the content of your landing page or blog. Let’s begin by creating a markdown file that will have some data we want to show on our home screen.</p>
<p>Make a <code>content</code> directory that will hold all our markdown files. Next, create a <code>pages</code> directory and a markdown file called <code>content/pages/home.md</code>. This file will hold the data that we want to show on our home page. Let’s start by adding a title, description, and image for the “hero” section of our landing page:</p>
<pre><code>---
hero_title: Build a SaaS faster with React
hero_description: Serverless SaaS is aiming to be the perfect starting point for your next React app to build full-stack SaaS applications. Visit serverless.page for more info.
---
</code></pre>
<p>Next, we need to tell webpack how to load markdown files. Let’s use <code>frontmatter-markdown-loader</code> for this. Start by installing that package as a dev dependency:</p>
<pre><code>yarn add frontmatter-markdown-loader -D
</code></pre>
<p>Next, create a new <code>next.config.js</code> file at the root of your project with the following content:</p>
<h3>Create the home page</h3>
<p>Cool, now let’s add some code to the index page to make it our home page.</p>
<p>In <code>pages/index.tsx</code>, add the following:</p>
<p>Let’s have a look at what’s going on here. The most important part is the exported <code>getStaticProps</code> function. If you export an <code>async</code> function called <code>getStaticProps</code> from a page, Next.js will pre-render this page at build time using the props returned by <code>getStaticProps</code>.</p>
<p>On our page, we import our markdown file called <code>home.md</code> and pass it a <code>content</code> prop to our component. Our home page can now use the <code>hero_title</code> and <code>hero_description</code> attributes of the markdown file.</p>
<p>Let’s try it out and see if it’s working:</p>
<pre><code>yarn dev
</code></pre>
<p>You should see your amazing hero section, ready to be styled and managed with your upcoming CMS.</p>
<h2>Implement Netlify CMS</h2>
<p>There are different ways to add Netlify CMS to your project. We are going to use the official npm package:</p>
<pre><code>yarn add netlify-cms-app
</code></pre>
<h3>Add the configuration</h3>
<p>Before we can initialize our CMS, we need to create a config file. Create a <code>cms</code> directory at the root of your project and paste the following configuration to your <code>cms/config.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">cms_manual_init</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  <span class="token literal-property property">backend</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;github&#x27;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">repo</span><span class="token operator">:</span> <span class="token string">&#x27;yourname/your-github-repo-name&#x27;</span><span class="token punctuation">,</span>
    <span class="token literal-property property">branch</span><span class="token operator">:</span> <span class="token string">&#x27;main&#x27;</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token literal-property property">media_folder</span><span class="token operator">:</span> <span class="token string">&#x27;public/img&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">public_folder</span><span class="token operator">:</span> <span class="token string">&#x27;img&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">collections</span><span class="token operator">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;pages&#x27;</span><span class="token punctuation">,</span>
      <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">&#x27;Pages&#x27;</span><span class="token punctuation">,</span>
      <span class="token literal-property property">files</span><span class="token operator">:</span> <span class="token punctuation">[</span>
        <span class="token punctuation">{</span>
          <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">&#x27;Home&#x27;</span><span class="token punctuation">,</span>
          <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;home&#x27;</span><span class="token punctuation">,</span>
          <span class="token literal-property property">file</span><span class="token operator">:</span> <span class="token string">&#x27;content/pages/home.md&#x27;</span><span class="token punctuation">,</span>
          <span class="token literal-property property">fields</span><span class="token operator">:</span> <span class="token punctuation">[</span>
            <span class="token punctuation">{</span>
              <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">&#x27;Hero Title&#x27;</span><span class="token punctuation">,</span>
              <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;hero_title&#x27;</span><span class="token punctuation">,</span>
              <span class="token literal-property property">widget</span><span class="token operator">:</span> <span class="token string">&#x27;string&#x27;</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token punctuation">{</span>
              <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">&#x27;Hero Description&#x27;</span><span class="token punctuation">,</span>
              <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;hero_description&#x27;</span><span class="token punctuation">,</span>
              <span class="token literal-property property">widget</span><span class="token operator">:</span> <span class="token string">&#x27;markdown&#x27;</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>
          <span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Netlify CMS has different options on how to handle authentication. We are going to use this config file to tell Netlify CMS that we want to use the GitHub for this. For repositories stored on GitHub, the <code>github</code> back end allows CMS users to log in directly with their GitHub account. Note that all users must have push access to their content repository for this to work.</p>
<p>If you haven’t done already, now would be a good time to <a href="https://github.com/new">create your repository on GitHub</a>, add the name of your repo to the config file, and push your code to GitHub. Go ahead — I’ll wait.</p>
<p>Good, but before we move on, let’s take a look at the most important setting of our configuration: the <code>collections</code>. This determines how content types and editor fields in the UI generate files and content in your repository.</p>
<p>Inside <code>collections</code>, we create a <code>pages</code> collection that has some settings for the <code>home</code> page. For now, we only added the <code>hero_title</code> and <code>hero_description</code> fields. You can set which <a href="https://www.netlifycms.org/docs/widgets/#header">widget</a> to use for the given property.</p>
<p>You can read about all other configuration options <a href="https://www.netlifycms.org/docs/configuration-options">in the documentation</a>.</p>
<h3>Initialize the CMS</h3>
<p>Next, let’s create an admin page for our CMS. Add the following code to <code>pages/admin.tsx</code>:</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token keyword module">import</span> <span class="token imports">dynamic</span> <span class="token keyword module">from</span> <span class="token string">&#x27;next/dynamic&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports">config</span> <span class="token keyword module">from</span> <span class="token string">&#x27;../cms/config&#x27;</span>

<span class="token keyword">const</span> <span class="token constant">CMS</span> <span class="token operator">=</span> <span class="token function">dynamic</span><span class="token punctuation">(</span>
  <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span>
    <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token string">&#x27;netlify-cms-app&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>cms<span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      cms<span class="token punctuation">.</span><span class="token method function property-access">init</span><span class="token punctuation">(</span><span class="token punctuation">{</span> config <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> ssr<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token function-variable function">loading</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">}</span>
<span class="token punctuation">)</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">AdminPage</span><span class="token operator">:</span> <span class="token maybe-class-name">React</span><span class="token punctuation">.</span><span class="token method-variable function-variable method function property-access"><span class="token maybe-class-name">FC</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">CMS</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">AdminPage</span>
</code></pre>
<p>Now you should be able to navigate to <a href="https://localhost:4202/">http://localhost:3000/admin</a> and see a GitHub login button. Hit the button and see your nice new CMS!</p>
<img alt="Blank CMS" class="t u v hn aj" src="https://miro.medium.com/max/700/1*sCq_bD6q9UjrCg-YhKpL_A.png" width="1058" height="369"/>
<h3>Try it out</h3>
<p>Cool, now try it out! Select “Home” and you will see the edit page. You can edit the <code>hero_title</code> and <code>hero_description</code> properties and click the <code>publish</code> button. When you publish, Netlify CMS is making a commit to your repository with the changes you just made. You can take a look at the last commit after you have published some changes.</p>
<p>Now, all you have to do is <code>git pull</code> to get those changes locally and navigate back to <a href="http://localhost:3000/">http://localhost:3000/</a>. You will see that the home page contains your changes. Sweet!</p>
<h2>Adding Images</h2>
<p>Most hero sections contain a pretty image. Let’s use the <code>image</code> widget to add an image to our hero section. Go to your <code>cms/config.js</code> file and add the following object to the <code>fields</code> array:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">{</span>
  <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">&#x27;Hero Image&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">&#x27;hero_image&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">widget</span><span class="token operator">:</span> <span class="token string">&#x27;image&#x27;</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Next, we could simply add an image to our <code>public/img</code> folder and add it to our <code>home.md</code>, but to demonstrate how Netlify CMS works, we are going to use the CMS to do it.</p>
<p>First, we need to upload an image to our integrated media library. On the dashboard of the CMS, click the “Media” link in the top navbar and upload an image. After that, navigate to the edit screen of the home page.</p>
<p>You should now see the added <code>hero_image</code> field with a button that says, “Choose an image.” You can now select your uploaded image and publish the changes.</p>
<p>Great, we should now have a new commit to our repo with the new changes. If our application was already in production, we could see the changes live, but since we want to see these changes locally, we need to first pull them from GitHub.</p>
<p>After you have run <code>git pull</code>, there is really only one thing left to do, which is adding the image to our home page:</p>
<pre class="language-tsx"><code class="language-tsx"><span class="token spread operator">...</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>attributes<span class="token punctuation">.</span><span class="token property-access">hero_image</span><span class="token punctuation">}</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&#x27;</span>hero image<span class="token punctuation">&#x27;</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token spread operator">...</span>
</code></pre>
<p>Now, run <code>yarn dev</code> and go to <a href="http://localhost:3000/">http://localhost:3000/</a> to see your added image!</p>
<h2>Conclusion</h2>
<p>Netlify CMS is a very useful library you can add to your Next.js apps. It’s just a client-side React application, and it uses Git to store content in your own repository. This has some nice benefits: You don’t need to host your CMS separately and it fits perfectly in your Git workflow. This is great for managing the content of a landing page or blog post.</p>
<p>If you are looking for a complete boilerplate, check out <a href="http://serverless.page/">serverless.page</a>.</p>
<h2>Next Steps</h2>
<p>We have covered a lot in this tutorial, but we have only seen the basics of how Netlify CMS works in a Next.js application. There is a lot more we can do! Stay tuned for upcoming articles to continue our work with:</p>
<ul>
<li>A blog</li>
<li>More widgets for managing content</li>
<li>Styling and CMS previews</li>
<li>Customizing with additional configuration settings</li>
</ul>
<p>View the source code of this tutorial <a href="https://github.com/jakeprins/nextjs-netlify-cms">on GitHub</a>.</p>
<p>Thanks for reading!</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Set Up Tailwind CSS V2.0 With Next.js 10]]></title>
            <link>https://jakeprins.com/blog/how-to-set-up-tailwind-css-v2-0-with-next-js-10</link>
            <guid>https://jakeprins.com/blog/how-to-set-up-tailwind-css-v2-0-with-next-js-10</guid>
            <pubDate>Thu, 15 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[In the last months of 2020 a couple of great framework updates have been released, like Next.js 10 and Tailwind v2.0. Let’s learn how to use these cool frameworks in a new React project.]]></description>
            <content:encoded><![CDATA[<p>In the last months of 2020, a couple of great framework updates have been released. First was the release of <a href="https://nextjs.org/blog/next-10">Next.js 10</a>, which came with a lot of nice features, like the new image component that automatically optimizes images.</p>
<p>Secondly, 18 months after the 1.0 release, the Tailwind team released <a href="https://blog.tailwindcss.com/tailwindcss-v2">v2.0</a> of their CSS framework. This new update included lots of improvements like a <a href="https://blog.tailwindcss.com/tailwindcss-v2#all-new-color-palette">new color palette</a> and <a href="https://blog.tailwindcss.com/tailwindcss-v2#dark-mode">dark mode support</a>.</p>
<p>Using the latest version of Next.js with Tailwind CSS is a very powerful combination to create and style web applications. Let’s walk through the steps of setting a new project up.</p>
<h3>Start a New Next.js Project</h3>
<p>Assuming you have installed <a href="https://classic.yarnpkg.com/en/docs/install/#mac-stable">Yarn</a>, open your terminal and run <code>yarn create next-app</code> in your projects folder.</p>
<p>You will get prompted with the following message:</p>
<pre><code>What is your project named?
</code></pre>
<p>Fill in a name, hit enter, and wait until your project is ready. Then, type <code>cd &lt;your-project-name&gt;</code> to make sure you are inside that directory where you can run <code>yarn dev</code> to start the development server. You should now have your new Next.js 10 project up and running.</p>
<h3>Install Tailwind and Its Dependencies</h3>
<p>Tailwind CSS is a plugin built on <a href="https://postcss.org/">PostCSS</a>, a tool for transforming CSS with JavaScript. The v2.0 has been updated for the latest PostCSS release, which requires installing <code>postcss</code> and <code>autoprefixer</code> as peer dependencies alongside Tailwind itself.</p>
<p>Add Tailwind and install PostCSS as well as autoprefixer using npm or yarn:</p>
<pre><code>yarn add tailwindcss postcss autoprefixer
</code></pre>
<h3>Create Config Files</h3>
<p>We need to add a <code>tailwind.config.js</code> and a <code>postcss.config.js</code> file to the root of our application. Use the following command to set this up:</p>
<pre><code>npx tailwindcss init -p
</code></pre>
<p>This will create a <code>tailwind.config.js</code> file at the root of your project:</p>
<p>Learn more about configuring Tailwind in the <a href="https://tailwindcss.com/docs/configuration">configuration documentation</a>.</p>
<p>It will also create a <code>postcss.config.js</code> file that includes <code>tailwindcss</code> and <code>autoprefixer</code> configured:</p>
<h3>Import the CSS</h3>
<p>Let’s create a <code>styles</code> folder and import Tailwind CSS from a CSS file:</p>
<pre><code>touch styles/tailwind.css
</code></pre>
<p>Inside <code>tailwind.css</code>:</p>
<pre><code>@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<p>To add global CSS to a Next.js app, we need to override the default <code>App</code> component. With Next.js 10, you should already have <code>_app.js</code> inside your pages folder. Now import the stylesheet we created:</p>
<pre><code>import &#x27;../styles/globals.css&#x27;
import &#x27;../styles/tailwind.css&#x27;;export default function MyApp({ Component, pageProps }) {
  return &lt;Component {...pageProps} /&gt;;
}
</code></pre>
<p>Cool, now we are ready to add some Tailwind CSS magic to our home page. Go to <code>/pages/index.js</code> (or <code>/pages/index.tsx</code> if you use TypeScript) and add some elements with Tailwind CSS classes. For example:</p>
<p>Run <code>yarn dev</code> to see your app on <a href="https://localhost:4202/settings/departments">http://localhost:3000</a> in your browser.</p>
<h3>Configure PurgeCSS</h3>
<p>One problem with Tailwind CSS is the large file size, but <a href="https://github.com/FullHuman/purgecss">PurgeCSS</a> can fix this. PurgeCSS reduces the file size by scanning your HTML and removing any classes that aren’t used. We only want this in production because if we are developing, we want to be able to use any Tailwind CSS class without running the build process.</p>
<p>Now with Tailwind CSS v2, PurgeCSS is already included. All you have to do is update the <code>tailwind.config.js</code> file so Tailwind can tree-shake unused styles in production builds. Update your file like this:</p>
<p>For now, we check all of our code inside <code>.js, .jsx, .ts or .tsx</code> files that live in either the <code>pages/</code> or <code>components/</code> folder. If you plan to add HTML in other folders like <code>containers/</code> or something, make sure you add that folder to this configuration file.</p>
<p>You can read the guide from Tailwind on <a href="https://tailwindcss.com/docs/optimizing-for-production">optimizing for production</a> to learn more about tree-shaking unused styles for best performance.</p>
<h3>Conclusion</h3>
<p>Now we are ready to work with the latest versions of Next.js and Tailwind CSS without having to worry about bundle sizes!</p>
<p>That’s it! Thanks for reading. I hope it was helpful.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Implement User Authentication With Next.js and Firebase]]></title>
            <link>https://jakeprins.com/blog/implement-user-authentication-with-next-js-and-firebase</link>
            <guid>https://jakeprins.com/blog/implement-user-authentication-with-next-js-and-firebase</guid>
            <pubDate>Fri, 28 Aug 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Auth doesn’t have to be hard or time-consuming. In this tutorial, we are going to use Firebase to quickly implement our own authentication flow. We will do this using Next.js, Typescript, and TailwindCSS.]]></description>
            <content:encoded><![CDATA[<p>Building your own authentication system can be a difficult and time-consuming task. Using existing platforms to handle your authentication flow for you will save you a lot of time and doesn’t even have to cost you any money. The free plan of Firebase, one of the most popular platforms than can handle authentication for you, allows any developer to quickly build secure authentication systems easily. It helps you build applications fast, without managing infrastructure. In this tutorial, we are going to use this platform to quickly implement our own authentication flow. We will do this using Next.js, Typescript, and TailwindCSS.</p>
<p>To follow along, you should have an existing project. You can follow these other tutorials on Better Programming to set your project up:</p>
<ul>
<li><a href="https://jakeprins.com/blog/how-to-set-up-next-js-with-typescript">Start your new React project with Next.js and TypeScript</a></li>
<li><a href="https://jakeprins.com/blog/how-to-set-up-next-js-with-tailwind-css">Set Up Next.js With Tailwind CSS</a></li>
<li><a href="https://jakeprins.com/blog/how-to-use-eslint-and-prettier-in-your-next-js-application-with-typescript">Set Up Next.js With Eslint and Prettier</a></li>
</ul>
<hr/>
<h2>Overview</h2>
<p>In this tutorial, we are going to implement the following things:</p>
<ul>
<li>Implement a login, sign up, and reset password form.</li>
<li>Store additional user data in a Firestore DB and fetch this data whenever the user logs in..</li>
<li>Created a useAuth hook and use the Context API to provide the user data to all our components.</li>
<li>Show a loading spinner icon whenever we are fetching data.</li>
<li>Show error messages whenever something goes wrong.</li>
<li>Created a hook to redirect the user when entering an authenticated and not being logged in.</li>
<li>Create a logout button.</li>
</ul>
<hr/>
<h2>Create the Login and Sign Up Pages</h2>
<p>Let’s start by setting up our authentication pages and make sure we can navigate between these in our app.</p>
<p>Create a file <code>/pages/signup.tsx</code>:</p>
<p>Let’s do the same for out <code>Login</code> page (<code>/pages/login.tsx</code>) and make sure that we can navigate between the two.</p>
<p>If you haven’t done it yet, start your server with <code>yarn dev</code> and navigate to <code>/login</code> in your browser to test if the page works.</p>
<hr/>
<h2>Create the Forms</h2>
<p>Now that we have our pages, let’s create the actual sign up and login forms. A great library for handling forms in React is <a href="https://react-hook-form.com">react-hook-form</a>. It helps you built performant, flexible, and extensible forms with easy-to-use validation. Let&#x27;s install it first:</p>
<pre><code>yarn add react-hook-form
</code></pre>
<p>Now we can create our sign up form:</p>
<p>Now import the <code>SignUpForm</code> component inside <code>pages/signup.tsx</code> and Replace the &quot;todo&quot; comment with the just created <code>&lt;SignUpForm /&gt;</code>.</p>
<p>You can test the form by submitting the form and checking the logs in the console. It should show you an object that looks like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
  name<span class="token operator">:</span> Jake<span class="token punctuation">,</span>
  email<span class="token operator">:</span> jake@prins.com<span class="token punctuation">,</span>
  password<span class="token operator">:</span> secretpassword<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Also, try out if the validations are working. All fields should be required. The email should be validated to have the right format, and the password should at least be 6 characters long.</p>
<p>We are doing great, let’s do the same with the login form.</p>
<p>Create <code>/components/forms/LoginForm.tsx</code> and copy and paste your sign up form code. Change the name to <code>LoginForm</code> and remove the &quot;name&quot; input, because we only need email and password to log in. Also, adjust the <code>LoginData</code> interface</p>
<p>Your form should look like this:</p>
<hr/>
<h2>Create a Firebase Project</h2>
<p>Now that we have our forms ready, we need to integrate them with Firebase. Let’s start by setting up a Firebase Project.</p>
<p>First, we need to create a Firebase account. Go to <a href="https://firebase.google.com/">https://firebase.google.com/</a>, click the “Get started” button, and follow the instructions to create your project.</p>
<p>Once your project is created, you should register your app inside the Firebase console. From the project overview page, click the web icon to add Firebase to your web application. Once created, you will receive your firebase config, which should look something like this:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> firebaseConfig <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token literal-property property">apiKey</span><span class="token operator">:</span> <span class="token string">&#x27;xxxx&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">authDomain</span><span class="token operator">:</span> <span class="token string">&#x27;your-project-name.firebaseapp.com&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">databaseURL</span><span class="token operator">:</span> <span class="token string">&#x27;&lt;https://your-project-name.firebaseio.com&gt;&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">projectId</span><span class="token operator">:</span> <span class="token string">&#x27;your-project-name&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">storageBucket</span><span class="token operator">:</span> <span class="token string">&#x27;your-project-name.appspot.com&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">messagingSenderId</span><span class="token operator">:</span> <span class="token string">&#x27;xxx&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">appId</span><span class="token operator">:</span> <span class="token string">&#x27;xxx&#x27;</span><span class="token punctuation">,</span>
  <span class="token literal-property property">measurementId</span><span class="token operator">:</span> <span class="token string">&#x27;G-xxx&#x27;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre>
<p>We should now activate the sign-up methods that we would like to add to our app. Navigate to <em>Authentication</em> and start by activating the <em>Email/password</em> method.</p>
<h2>Add Firestore (optional)</h2>
<p>If you want to save additional information during sign up, like a username or full name, you need to set up a database to save that data. <a href="https://firebase.google.com/docs/firestore">Cloud Firestore</a> is a flexible, scalable database from Firebase. It offers seamless integration with Firebase and other Google Cloud Platform products, like Cloud Functions. And just like Firebase, it starts completely free. Only when your application really starts to scale, you might exceed the free plan, but even then you only pay for what you use. A very interesting price model if you don’t want to spend a lot (or any) money when you are just starting out.</p>
<p>If you want to setup Firestore then navigate to <em>Database</em> and click the first <em>Create database</em> button to add Cloud Firestore to your project. Select the option to start in test mode. We will worry about the Firestore rules later.</p>
<hr/>
<h2>Implement Firebase in Your Project</h2>
<p>We are now ready to implement Firebase in our project.</p>
<p>First, install Firebase:</p>
<pre><code>yarn add firebase
</code></pre>
<p>We need the save our Firebase configuration to some <a href="https://nextjs.org/docs/basic-features/environment-variables">environment variables</a>.</p>
<p>Next.js comes with built-in support for environment variables, which allows you to use <code>.env.local</code> to load <a href="https://nextjs.org/docs/basic-features/environment-variables#loading-environment-variables">environment variables</a> and <a href="https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser">expose environment variables to the browser</a>.</p>
<p>Go to your <code>.env.local</code> file (or create it if it&#x27;s not there yet). Then, past in the example below and change the dummy data with your own Firebase credentials.</p>
<pre><code>NEXT_PUBLIC_FIREBASE_API_KEY=&quot;yourapikey&quot;
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=&quot;yourappname.firebaseapp.com&quot;
NEXT_PUBLIC_FIREBASE_DATABASE_URL=&quot;&lt;https://appname.firebaseio.com&gt;&quot;
NEXT_PUBLIC_FIREBASE_PROJECT_ID=&quot;yourappname&quot;
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=&quot;yourappname.appspot.com&quot;
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=&quot;yoursenderid&quot;
NEXT_PUBLIC_FIREBASE_APP_ID=&quot;yourappid&quot;
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=&quot;yourmeasurementid&quot;
</code></pre>
<p><strong>Only if you are on a Next.js version lower then 9.4</strong> you should create a <code>next.config.ts</code> file and export your environment variables yourself.</p>
<p>Great, we can now access our data like this: <code>process.env.NEXT_PUBLIC_FIREBASE_API_KEY</code>. Double-check if <code>.env.local</code> is added to your <code>.gitignore</code>, so you don&#x27;t accidentally commit this data. Also keep in mind that when you deploy your application, you first need to set your production environment variables. When deploying on <a href="https://vercel.com">Vercel</a> you can configure secrets in the <a href="https://vercel.com/docs/v2/build-step#environment-variables">Environment Variables</a> section of the project in the Vercel dashboard.</p>
<p>Next up, create a file <code>/config/firebase.ts</code> and add your configuration.</p>
<p>In this file, we first declare our Firebase configuration and then initialize Firebase in our project with <code>firebase.initializeApp(firebaseConfig)</code>.</p>
<p>As you can see, we also exporting some functions that we are going to need when we’re developing our application.</p>
<hr/>
<h1>Sign Up</h1>
<p>Now that we are ready to use Firebase in our project, let’s go to our <code>SignUpForm</code> component and start by creating our <code>signUp</code> function.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> auth <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;config/firebase&#x27;</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>
<span class="token keyword">const</span> <span class="token function-variable function">signUp</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> name<span class="token punctuation">,</span> email<span class="token punctuation">,</span> password <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
 <span class="token keyword control-flow">return</span> auth
  <span class="token punctuation">.</span><span class="token method function property-access">createUserWithEmailAndPassword</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> error <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>Now you can try out your form:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">onSubmit</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token maybe-class-name">SignUpData</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token function">signUp</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">user</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>If you now try out the form, you will sign up for your firebase project. Go to your Firebase console, and go to the Authentication overview. You should see the submitted data here.</p>
<p>Amazing! With just those couple of lines, you built a sign-up form!</p>
<p>That’s great, but what happens to the <em>name</em> property? Well, if we want to store additional data, we also want to create the user in our Firestore database. So let’s create a function that creates the user document in our database and make sure that the UID of the record is the same.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> auth <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;config/firebase&#x27;</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>

<span class="token keyword">const</span> <span class="token function-variable function">createUser</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">user</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
 <span class="token keyword control-flow">return</span> db
  <span class="token punctuation">.</span><span class="token method function property-access">collection</span><span class="token punctuation">(</span><span class="token string">&#x27;users&#x27;</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">doc</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token property-access">uid</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">set</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span><span class="token string">&quot;Success&quot;</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">log</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>We can now call this function as soon as the user has successfully signed up. Go ahead and update your <code>signUp</code> function:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">signUp</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> name<span class="token punctuation">,</span> email<span class="token punctuation">,</span> password <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth
    <span class="token punctuation">.</span><span class="token method function property-access">createUserWithEmailAndPassword</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token function">createUser</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">uid</span><span class="token operator">:</span> response<span class="token punctuation">.</span><span class="token property-access">user</span><span class="token punctuation">.</span><span class="token property-access">uid</span><span class="token punctuation">,</span> email<span class="token punctuation">,</span> name <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> error <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>If you now try again, you should get an error message, because you cannot sign up twice with the same email address. Make sure you have deleted the first entry in your <a href="https://console.firebase.google.com/">Firebase console</a> and then try again (or try with a different email).</p>
<p>After submitting the sign-up form, go and check inside <em>Authentication</em> and <em>Database</em> in the <a href="https://console.firebase.google.com/">Firebase console</a> to see if both records are created.</p>
<p>Great, you should now have a working sign up form!</p>
<h2>Handle state</h2>
<p>Based on the success response we can now redirect the user to a different route after they signed up, but the only problem is that we are going to lose our state. On the redirected page, we still need to be able to tell if the user is logged in. We also want to use the data of the current user in multiple places across our application.</p>
<p>At this point, we need to think about the global state. We could use a state management library like <a href="https://redux.js.org/">Redux</a>, but that also introduces a lot of complexity and isn’t always what you want. Especially now that React has Context and Hooks, we don’t really need a state management library at this point.</p>
<hr/>
<h2>Create an Authentication Provider</h2>
<p><a href="https://reactjs.org/docs/context.html">Context</a> provides a way to pass data through the component tree without having to pass props down manually at every level.</p>
<p>Let’s start by creating our <code>useAuthProvider</code> hook and move our <code>signUp</code> and <code>createUser</code> functions to this hook. We also set the user state after success or error with <code>useState</code>.</p>
<p>Create a file called <code>hooks/useAuth</code> and add the following code:</p>
<p>Now let’s use <a href="https://reactjs.org/docs/context.html">Context</a> to create a Provider. Add the following code above the code you just placed in <code>hooks/useAuth.tsx</code></p>
<p>Great — now wrap your <code>AppComponent</code> with this provider so we use this <code>useAuth</code> hook everywhere in our app. In your <code>_app.tsx</code>:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">AppProps</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next/app&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">AuthProvider</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;hooks/useAuth&#x27;</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">MyApp</span></span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> <span class="token maybe-class-name">Component</span><span class="token punctuation">,</span> pageProps <span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">AppProps</span></span><span class="token punctuation">)</span><span class="token operator">:</span> any <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">AuthProvider</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Component</span></span> <span class="token spread"><span class="token punctuation">{</span><span class="token spread operator">...</span>pageProps<span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">AuthProvider</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Let’s also make a page that we can redirect to if we are successfully logged in: <code>pages/dashboard.tsx</code>.</p>
<p>Cool, now let’s try it out! If you want to try again with the same email then first delete your user data from Firebase before you submit the form (or use a different email). If everything is okay you should now be redirected to the dashboard page.</p>
<p>Noice! This is starting to look more like it. Let’s use our momentum and keep going.</p>
<hr/>
<h2>Login</h2>
<p>Let’s create our login feature. Inside <code>useAuth.tsx</code> add the following function.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">signIn</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> email<span class="token punctuation">,</span> password <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth
    <span class="token punctuation">.</span><span class="token method function property-access">signInWithEmailAndPassword</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token function">setUser</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token property-access">user</span><span class="token punctuation">)</span>
      <span class="token keyword control-flow">return</span> response<span class="token punctuation">.</span><span class="token property-access">user</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> error <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Make sure you export it.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> user<span class="token punctuation">,</span> signUp<span class="token punctuation">,</span> signIn <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>
</code></pre>
<p>Now inside <code>LoginForm.tsx</code> call that function on submit:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword">const</span> <span class="token literal-property property">LoginForm</span><span class="token operator">:</span> <span class="token maybe-class-name">React</span><span class="token punctuation">.</span><span class="token method-variable function-variable method function property-access"><span class="token maybe-class-name">FC</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
 <span class="token keyword">const</span> <span class="token punctuation">{</span> register<span class="token punctuation">,</span> errors<span class="token punctuation">,</span> handleSubmit <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">useAuth</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token function-variable function">onSubmit</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token maybe-class-name">LoginData</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth<span class="token punctuation">.</span><span class="token method function property-access">signIn</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   router<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token string">&#x27;/dashboard&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span><span class="token punctuation">;</span>
 <span class="token spread operator">...</span>
</code></pre>
<p>Cool, now let’s try if this works.</p>
<p>You should now be able to log in, accept we are missing the <em>name</em>. This is because the name is not coming from Firebase Authentication, but is stored in our Firestore database. We need to listen to the auth state change and get the data from the DB.</p>
<p>Inside the <code>useAuthProvider</code> hook add this function:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">getUserAdditionalData</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">user</span><span class="token operator">:</span> firebase<span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">User</span></span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> db
    <span class="token punctuation">.</span><span class="token method function property-access">collection</span><span class="token punctuation">(</span><span class="token string">&#x27;users&#x27;</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">doc</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token property-access">uid</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">userData</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>userData<span class="token punctuation">.</span><span class="token method function property-access">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token function">setUser</span><span class="token punctuation">(</span>userData<span class="token punctuation">.</span><span class="token method function property-access">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Then, call this function after a successful sign-in request.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">signIn</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> email<span class="token punctuation">,</span> password <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth
    <span class="token punctuation">.</span><span class="token method function property-access">signInWithEmailAndPassword</span><span class="token punctuation">(</span>email<span class="token punctuation">,</span> password<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token function">setUser</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token property-access">user</span><span class="token punctuation">)</span>
      <span class="token function">getUserAdditionalData</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>
      <span class="token keyword control-flow">return</span> response<span class="token punctuation">.</span><span class="token property-access">user</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> error <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Great, now this should fix our problem. But it does not fix everything, because Firebase also helps us to keep users logged in. But after the user is logged in when they re-enter our application, we also need to fetch that additional data.</p>
<p>So let’s also subscribe to the <code>onAuthStateChanged</code> event and call <code>getUserAdditionalData</code> to fetch that data:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">handleAuthStateChanged</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">user</span><span class="token operator">:</span> firebase<span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">User</span></span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">setUser</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>
  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>user<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">getUserAdditionalData</span><span class="token punctuation">(</span>user<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> unsubscribe <span class="token operator">=</span> auth<span class="token punctuation">.</span><span class="token method function property-access">onAuthStateChanged</span><span class="token punctuation">(</span>handleAuthStateChanged<span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">unsubscribe</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre>
<p>If you refresh the page, you should see your name. Magic! 🎩</p>
<p>To improve this some more, we could also make sure that whenever the user’s document is updated, we also update the user state in our application. We can do this by adding another effect to the useAuth hook that subscribes to the user’s document and updates the state whenever it changes.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>user<span class="token operator">?.</span>uid<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// Subscribe to user document on mount</span>
    <span class="token keyword">const</span> unsubscribe <span class="token operator">=</span> db
      <span class="token punctuation">.</span><span class="token method function property-access">collection</span><span class="token punctuation">(</span><span class="token string">&#x27;users&#x27;</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">doc</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token property-access">uid</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token method function property-access">onSnapshot</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">doc</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">setUser</span><span class="token punctuation">(</span>doc<span class="token punctuation">.</span><span class="token method function property-access">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">unsubscribe</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre>
<hr/>
<h2>Logout</h2>
<p>We are now making sure we fetch the user data from our DB and update our local state so the user knows he or she is logged in. This is great, but we are not done yet. Let’s also make sure that the user can log out.</p>
<p>Add this <code>signOut</code> function to the <code>useAuthProvider</code>:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword">const</span> <span class="token function-variable function">signOut</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth<span class="token punctuation">.</span><span class="token method function property-access">signOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">setUser</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token spread operator">...</span>
</code></pre>
<p>We want to be able to call this function anywhere, so don’t forget to export it.</p>
<pre><code>...
return { user, signUp, signIn, signOut };
...
</code></pre>
<p>Now, all we have to do is make a button that calls this function when it’s clicked. Add this button to your <code>pages/dashboard.tsx</code>:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token operator">&lt;</span>button
 onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> auth<span class="token punctuation">.</span><span class="token method function property-access">signOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
 className<span class="token operator">=</span><span class="token string">&quot;w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out&quot;</span>
\<span class="token operator">&gt;</span>
 <span class="token maybe-class-name">Sign</span> out
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
</code></pre>
<p>Try it out!</p>
<p>If it’s working, you will end up with a white screen. That’s not really what we want. We actually never want users to be able to enter <code>/dashboard</code> if they are not logged in, so let&#x27;s make a hook that will redirect users if they are not logged in.</p>
<p>Create the following hook at <code>hooks/useRequireAuth.tsx</code></p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useEffect <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useRouter <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;next/router&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useAuth <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;hooks/useAuth&#x27;</span>

<span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">useRequireAuth</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">useAuth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>auth<span class="token punctuation">.</span><span class="token property-access">user</span> <span class="token operator">===</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      router<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token string">&#x27;/login&#x27;</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>auth<span class="token punctuation">,</span> router<span class="token punctuation">]</span><span class="token punctuation">)</span>

  <span class="token keyword control-flow">return</span> auth
<span class="token punctuation">}</span>
</code></pre>
<p>Now use this hook inside <code>dashboard.tsx</code> . Replace the old <code>const auth = useAuth()</code> with our new hook:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">useRequireAuth</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token spread operator">...</span>
</code></pre>
<p>Again, magic! 🎩</p>
<p>You are doing amazing. We build a working sign up and login flow. Nice! Now grab some coffee, you deserved it. When you come back, we still need to code some more.</p>
<hr/>
<h2>Reset Password</h2>
<p>Because what happens when a user forgets their password? We need to handle this as well, so let’s add a function to our <code>useAuth</code> hook to send a reset password link.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token function-variable function">sendPasswordResetEmail</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">email</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> auth<span class="token punctuation">.</span><span class="token method function property-access">sendPasswordResetEmail</span><span class="token punctuation">(</span>email<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
   <span class="token keyword control-flow">return</span> response<span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span><span class="token punctuation">;</span>
 <span class="token spread operator">...</span>

<span class="token keyword control-flow">return</span> <span class="token punctuation">{</span>
  user<span class="token punctuation">,</span>
  signUp<span class="token punctuation">,</span>
  signIn<span class="token punctuation">,</span>
  signOut<span class="token punctuation">,</span>
  sendPasswordResetEmail<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>Now create a <code>ResetPasswordForm</code> component where the user can enter their email. On submit it should call that new <code>sendPasswordResetEmail</code> function.</p>
<p>Great. Now create the <code>reset-password</code> page that will show our new <code>ResetPasswordForm</code> component.</p>
<p>Also, add a link to this new <code>reset-password</code> page on our login form. Add this code right below the password input:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token spread operator">...</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mt-4 flex items-end<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
 </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text-sm leading-5<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
  </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/reset-password<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
   </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span>
    <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>#<span class="token punctuation">&quot;</span></span>
    <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>font-medium text-indigo-600 hover:text-indigo-500 focus:outline-none focus:underline transition ease-in-out duration-150<span class="token punctuation">&quot;</span></span>
   <span class="token punctuation">&gt;</span></span><span class="token plain-text">
    Forgot your password?
   </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
  </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
 </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token spread operator">...</span>
</code></pre>
<p>Nice. Now test things out and see your brand new authentication flow!</p>
<p>here is always room for some improvements. Let’s handle our loading states and error messages.</p>
<hr/>
<h2>Loading States and Error Messages</h2>
<p>It’s great that we now have a working authentication flow, but there is still a lot of room for improvement. Let’s start by handling the loading states and make sure we show an error message when something goes wrong.</p>
<p>Inside your <code>SignInForm</code> component, import <code>useState</code> and use it to set the state for <code>isLoading</code> and <code>error</code>. When we submit the form we set <code>isLoading</code> to <code>true</code> and clear the error. After the response is returned we set <code>isLoading</code> back to <code>false</code> and only if an error is present we set the error.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> useState <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span><span class="token punctuation">;</span><span class="token keyword">const</span> \<span class="token punctuation">[</span>isLoading<span class="token punctuation">,</span> setIsLoading\<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token punctuation">[</span>error<span class="token punctuation">,</span> setError<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token keyword null nil">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> <span class="token function-variable function">onSubmit</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">data</span><span class="token operator">:</span> <span class="token maybe-class-name">LoginData</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
 <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token function">setError</span><span class="token punctuation">(</span><span class="token keyword null nil">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token keyword control-flow">return</span> auth<span class="token punctuation">.</span><span class="token method function property-access">signIn</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token method function property-access">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  response<span class="token punctuation">.</span><span class="token property-access">error</span> <span class="token operator">?</span> <span class="token function">setError</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token property-access">error</span><span class="token punctuation">)</span> <span class="token operator">:</span> router<span class="token punctuation">.</span><span class="token method function property-access">push</span><span class="token punctuation">(</span><span class="token string">&#x27;/dashboard&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
 <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
<p>If the error is present, we want to show it inside our form:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token punctuation">{</span>
  error<span class="token operator">?.</span>message <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mb-4 rounded border border-dashed border-red-600 p-2 text-center text-red-500<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>error<span class="token punctuation">.</span><span class="token property-access">message</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>We also like to show the user that we are fetching data, so let’s change the text of our submit button whenever <code>isLoading</code> is <code>true</code>.</p>
<p>Because we are using this button on all of our forms, let’s create a reusable button component that we can import in different places.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">interface</span> <span class="token class-name">ButtonProps</span> <span class="token punctuation">{</span>
  title<span class="token operator">?</span><span class="token operator">:</span> string<span class="token punctuation">;</span>
  isLoading<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">const</span> <span class="token maybe-class-name">Button</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
  isLoading<span class="token punctuation">,</span>
  title<span class="token punctuation">,</span>
  children<span class="token punctuation">,</span>
  <span class="token spread operator">...</span>buttonProps
<span class="token punctuation">}</span><span class="token operator">:</span> <span class="token maybe-class-name">ButtonProps</span> <span class="token operator">&amp;</span>
  <span class="token maybe-class-name">React</span><span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">ButtonHTMLAttributes</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">HTMLButtonElement</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">): JSX.Element =&gt; </span><span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span>
      <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>focus:shadow-outline-indigo flex w-full justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white transition duration-150 ease-in-out hover:bg-indigo-500 focus:border-indigo-700 focus:outline-none active:bg-indigo-700<span class="token punctuation">&quot;</span></span>
      <span class="token spread"><span class="token punctuation">{</span><span class="token spread operator">...</span>buttonProps<span class="token punctuation">}</span></span>
    <span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token punctuation">{</span>isLoading <span class="token operator">?</span> <span class="token string">&#x27;Loading...&#x27;</span> <span class="token operator">:</span> title<span class="token punctuation">}</span><span class="token plain-text">
      </span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token plain-text">

export default Button
</span></code></pre>
<p>Now we can use our button inside our form components like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Button</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;components/elements/Button&#x27;</span><span class="token punctuation">;</span>

<span class="token spread operator">...</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Button</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>Login<span class="token punctuation">&quot;</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>submit<span class="token punctuation">&quot;</span></span> <span class="token attr-name">isLoading</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>isLoading<span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token spread operator">...</span>
</code></pre>
<p>That’s a lot better. We can make it even nicer by showing a spinner icon instead of the “Loading…” text.</p>
<p>At <code>icons/spinner.tsx</code>, create the spinner icon component:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token maybe-class-name">Spinner</span> <span class="token operator">=</span> <span class="token punctuation">(</span>props<span class="token operator">:</span> <span class="token maybe-class-name">React</span><span class="token punctuation">.</span><span class="token property-access"><span class="token maybe-class-name">SVGProps</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">SVGSVGElement</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">): JSX.Element =&gt; </span><span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>0 0 100 100<span class="token punctuation">&quot;</span></span> <span class="token spread"><span class="token punctuation">{</span><span class="token spread operator">...</span>props<span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>M90.9 58.2c-3.8 18.9-20.1 32.7-39.7 33-22 .4-40.7-17-41.9-39C8 28.5 26.9 8.8 50.4 8.8c19 0 35.5 13.1 40 31.2.3 1.2 1.4 2.1 2.7 2.1 1.8 0 3.1-1.7 2.7-3.5C90.6 18.1 72 3.3 50.4 3.3c-27.2 0-49 23.4-46.6 51.1 2.1 23 21 41.2 44 42.4C71.6 98 91.7 81.9 96.2 59.4c.3-1.7-1-3.3-2.7-3.3-1.3-.1-2.4.8-2.6 2.1z<span class="token punctuation">&quot;</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token plain-text">
export default Spinner
</span></code></pre>
<p>Now in our <code>Button</code> component, replace the loading text with the spinner component:</p>
<pre><code>{isLoading ? (
 &lt;Spinner width=&quot;20&quot; fill=&quot;white&quot; className=&quot;animate-spin&quot; /&gt;
) : (
 title
)}
</code></pre>
<p>Note: make sure you are at least on Tailwind version 1.7 so you can use the <code>animate-spin</code> class.</p>
<p>Noice! Now go ahead and do the same for the sign-up and reset-password forms and call it a day.</p>
<hr/>
<h2>Summary</h2>
<ul>
<li>We have implemented different pages for our authentication forms.</li>
<li>We have implement login, sign up, and reset password forms.</li>
<li>We store additional user data in a Firestore DB and fetch this data whenever the user logs in.</li>
<li>We implement a logout button.</li>
<li>We have created a useAuth hook and used the Context API to provide the user data to all our components.</li>
<li>We show a loading spinner icon whenever we are fetching data.</li>
<li>We show error messages whenever something goes wrong.</li>
<li>We have created a hook to redirect the user when entering an authenticated and not being logged in.</li>
</ul>
<p>That’s it! Thanks for reading. I hope it was helpful. Good luck with the next steps of your project.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Set Up Next.js With Tailwind CSS]]></title>
            <link>https://jakeprins.com/blog/how-to-set-up-next-js-with-tailwind-css</link>
            <guid>https://jakeprins.com/blog/how-to-set-up-next-js-with-tailwind-css</guid>
            <pubDate>Tue, 18 Aug 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[If you’re starting a new React project, you might want to consider Next.js and Tailwind. In this article, we will explain why this would be a great choice and walk you through the process of setting up a new project using these technologies.]]></description>
            <content:encoded><![CDATA[<p>If you’re starting a new React project, you might want to consider <a href="https://nextjs.org">Next.js</a> and <a href="https://tailwindcss.com">Tailwind CSS</a>. In this article, we will explain why this would be a great choice and walk you through the process of setting up a new project using these technologies.</p>
<hr/>
<em><p>UPDATE: Tailwind CSS 2.0 has been released. Please follow this new guide to
use the latest version of Tailwind with the latest version of Next.js:</p></em>
<p><a href="https://jakeprins.com/blog/how-to-set-up-tailwind-css-v2-0-with-next-js-10">How to Set Up Tailwind CSS V2.0 With Next.js 10</a></p>
<hr/>
<h2>Why Next.js?</h2>
<p>There are many important details you need to consider when you start a new project with React. Your code has to be bundled using a bundler like webpack and transformed using a compiler like Babel. <a href="https://reactjs.org/docs/create-a-new-react-app.html">Create React App</a> can be a nice tool to handle this for you and give you a massive head start, but what about code-splitting, pre-rendering for performance, and SEO or server-side rendering?</p>
<p>To build a complete React application, you need more than CRA provides you. You can save yourself some time by using Next.js, a React framework that provides a solution to all of these problems above. If you want to read more about CRA vs. Next.js, check out <a href="https://codeburst.io/stack-choices-create-react-app-vs-next-js-f0f3c4db7083">Stack choices: Create React App vs Next.js</a>.</p>
<hr/>
<h2>Why Tailwind CSS?</h2>
<p><a href="https://tailwindcss.com/">Tailwind CSS</a> is a CSS framework, but it’s different than most other frameworks like Bootstrap, Material, or Ant Design. It’s a utility-first framework, which means it doesn’t focus on predesigned components like buttons, cards, and alerts. It provides low-level utility classes that let you build completely custom designs without leaving your HTML.</p>
<p>Although it requires some time to learn all the utility classes, you barely need to write any CSS anymore once you start using it. It can give you a huge boost in productivity, and not needing to switch files while styling your HTML elements can feel like a blessing.</p>
<hr/>
<h2>Let’s Start</h2>
<p>If you haven’t got a project set up with Next.js, first open your terminal and run <code>yarn create next-app</code> in your projects folder.</p>
<p>You will get prompted with the following message:</p>
<pre><code>What is your project named?
</code></pre>
<p>Fill in a name and hit enter. The next message will be:</p>
<pre><code>Pick a template
</code></pre>
<p>Choose “Default starter app” and wait until your project is ready. Then, type <code>cd &lt;your-project-name&gt;</code> to make sure you are inside that directory where you can run <code>yarn dev</code> to start the development server. You should now have your new Next.js project up and running.</p>
<hr/>
<h1>Adding Tailwind CSS</h1>
<p>Start by adding the Tailwind CSS library to your development dependencies using npm or yarn:</p>
<pre><code>yarn add tailwindcss -D
</code></pre>
<p>Then, we need to add a <code>tailwind.config.js</code> file to the root of our application. Use the following command to set this up:</p>
<pre><code>npx tailwind init
</code></pre>
<p>You should now have a <code>tailwind.config.js</code> in your project containing the following:</p>
<hr/>
<h1>Adding PostCSS</h1>
<p>Next up, we need to configure <a href="https://postcss.org/">PostCSS</a>, a tool for transforming CSS with JavaScript because Tailwind CSS is a PostCSS plug-in. Let’s create a <code>postcss.config.js</code> and add the following so Next.js will load the defined plug-ins.</p>
<p>In the root of your project, run:</p>
<pre><code>touch postcss.config.js
</code></pre>
<p>Then add the following to that file:</p>
<pre><code>module.exports = {
  plugins: \[&#x27;tailwindcss&#x27;\],
}
</code></pre>
<p>Besides TailwindCSS, we should also add the <a href="https://github.com/csstools/postcss-preset-env">PostCSS Preset Env</a> module, which converts modern CSS into something most browsers understand:</p>
<pre><code>yarn add postcss-preset-env -D
</code></pre>
<p>Now let’s update our <code>postcss.config.js</code> to include this module:</p>
<pre><code>module.exports = {
  plugins: \[&#x27;tailwindcss&#x27;, &#x27;postcss-preset-env&#x27;\],
}
</code></pre>
<p>Now let’s create a <code>styles</code> folder and import Tailwind CSS from a CSS file:</p>
<pre><code>mkdir styles touch styles/tailwind.css
</code></pre>
<p>Inside <code>tailwind.css</code>:</p>
<pre><code>@tailwind base;
@tailwind components;
@tailwind utilities;
</code></pre>
<p>To add global CSS to a Next.js app, we need to override the default <code>App</code> component. Next.js uses the <code>App</code> component to initialize pages, and if we need to control the page initialization, we need to create a file called <code>pages/_app.js</code>:</p>
<pre><code>touch pages/\_app.js
</code></pre>
<p>Now import the stylesheet we created:</p>
<pre><code>import &#x27;../styles/tailwind.css&#x27;;export default function MyApp({ Component, pageProps }) {
  return &lt;Component {...pageProps} /&gt;;
}
</code></pre>
<p>Cool, now we are ready to add some Tailwind CSS magic to our home page. Go to <code>/pages/index.js</code> (or <code>/pages/index.tsx</code> if you use TypeScript) and add some elements with Tailwind CSS classes. For example:</p>
<p>Run <code>yarn dev</code> to see your app on <code>localhost:3000</code> in your browser.</p>
<hr/>
<h2>PurgeCSS</h2>
<p>One problem with Tailwind CSS is the large file size, but we can use <a href="https://github.com/FullHuman/purgecss">PurgeCSS</a> to fix this. PurgeCSS reduces the file size by scanning your HTML and removing any classes that aren’t used. We only want this in production because if we are developing, we want to be able to use any Tailwind CSS class without running the build process. To set this up, we start by installing the PurgeCSS module as a dev dependency in our project:</p>
<pre><code>yarn add @fullhuman/postcss-purgecss -D
</code></pre>
<p>Then update your <code>postcss.config.js</code> file with the following code:</p>
<p>The order of exports matters. First, we tell PostCSS to load Tailwind CSS. Then, we purge the unused CSS (only in production). Finally, we let the PostCSS Preset Env plug-in do its thing to make the remaining CSS compatible for all browsers.</p>
<p>For now, we check all of our code inside <code>.js, .jsx, .ts or .tsx</code> files that live in either the <code>pages/</code> or <code>components/</code> folder. If you plan to add HTML in other folders like <code>containers/</code> or something, make sure you add that folder to this configuration file.</p>
<p>Finally, it’s recommended to only apply PurgeCSS to Tailwind CSS’s utility classes — not to <a href="https://tailwindcss.com/docs/adding-base-styles">base styles</a> or <a href="https://tailwindcss.com/docs/extracting-components#extracting-css-components-with-apply">component classes</a>. This will ensure you don’t accidentally purge important base styles when working with Next.js The easiest way to do this is to use PurgeCSS’s <a href="https://github.com/FullHuman/purgecss-docs/blob/master/whitelisting.md">whitelisting</a> feature to disable PurgeCSS for non-utility classes. Add this to your <code>tailwind.css</code> file:</p>
<pre><code>/\* purgecss start ignore \*/
@tailwind  base;
@tailwind  components;
/\* purgecss end ignore \*/@tailwind  utilities;
</code></pre>
<p>Cool, now we are ready to work with Next.js and Tailwind CSS without having to worry about bundle sizes!</p>
<p>That’s it! Thanks for reading. I hope it was helpful.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Use ESLint and Prettier in Your Next.js Application With TypeScript]]></title>
            <link>https://jakeprins.com/blog/how-to-use-eslint-and-prettier-in-your-next-js-application-with-typescript</link>
            <guid>https://jakeprins.com/blog/how-to-use-eslint-and-prettier-in-your-next-js-application-with-typescript</guid>
            <pubDate>Fri, 17 Jul 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[If you’re starting a new React project, you might want to consider Next.js and Tailwind. In this article, we will explain why this would be a great choice and walk you through the process of setting up a new project using these technologies.]]></description>
            <content:encoded><![CDATA[<p>Automated lint tools can help developers save time and keep everybody on the same page. When everybody on your team follows the same rules, you don’t have to waste time discussing code style issues. These tools can catch a lot of syntax and type errors.</p>
<p>Combining <a href="https://eslint.org/">ESLint</a> with <a href="https://prettier.io/">Prettier</a> is a nice way to perform both automated syntax scans on your code and reformatting. This will ensure that consistent rules are being followed for indentation, spacing, semicolons, etc.</p>
<p>Let’s set up these technologies to use them in a <a href="https://nextjs.org/">Next.js</a> project that uses <a href="https://www.typescriptlang.org/">TypeScript</a>. If you are starting a new project, you can <a href="https://medium.com/better-programming/how-to-set-up-next-js-with-typescript-5f912766c88">read my article</a> about how to set up Next.js with TypeScript.</p>
<hr/>
<h3>ESLint</h3>
<p>Let’s start by adding the core ESLint linting library, the parser to lint TypeScript code, a TypeScript-specific plug-in, and a React-specific plug-in to our project.</p>
<p>In the terminal, go to the root of your project and run the following command:</p>
<pre><code>yarn add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react
</code></pre>
<p>We should now create a <code>.eslintrc.json</code> file at the root of our project. We can add our linting rules in this file:</p>
<pre><code>touch .eslintrc.json
</code></pre>
<p>You can already add the default options to that file. We will add our options as we go:</p>
<pre><code>{
  &quot;parser&quot;: {},
  &quot;extends&quot;: \[\],
  &quot;rules&quot;: {},
  &quot;globals&quot;: {}
}
</code></pre>
<p>By default, ESLint depends on a parser to read JavaScript code. We use <a href="https://github.com/typescript-eslint/typescript-eslint#readme">@typescript-eslint/parser</a> (an alternative parser that can read TypeScript), so we need to tell ESLint to use it instead. Besides that, we use <a href="https://github.com/typescript-eslint/typescript-eslint">@typescript-eslint/eslint-plugin</a> (a list of rules you can turn on or off). We also extend the plug-in <a href="https://github.com/yannickcr/eslint-plugin-react">react/recommended</a>, which contains React-specific linting rules.</p>
<p>To configure the parser and extend the plug-ins, we need to modify the file we need to update our config like this:</p>
<pre><code>{
  &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
  &quot;extends&quot;: [
    &quot;plugin:@typescript-eslint/recommended&quot;,
    &quot;plugin:react/recommended&quot;
  ],
  &quot;plugins&quot;: [
    &quot;@typescript-eslint&quot;,
    &quot;react/recommended&quot;
  ],
  &quot;rules&quot;: {},
  &quot;globals&quot;: {}
}
</code></pre>
<p>Next.js does not require you to import React into each component, which means you will start getting errors if you use this config in a Next.js application. We can fix this by adding <code>React</code> into our <code>global</code> and turning the <code>react-in-jsx-scope</code> rule off.</p>
<p>Our final config will look like this:</p>
<pre><code>{
  &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
  &quot;extends&quot;: [
    &quot;plugin:@typescript-eslint/recommended&quot;,
    &quot;plugin:react/recommended&quot;
  ],
  &quot;plugins&quot;: [
    &quot;@typescript-eslint&quot;,
    &quot;react&quot;
  ],
  &quot;rules&quot;: {
    &quot;react/react-in-jsx-scope&quot;: &quot;off&quot;
  },
  &quot;globals&quot;: {
    &quot;React&quot;: &quot;writable&quot;
  }
}
</code></pre>
<hr/>
<h3>Prettier</h3>
<p>Now let’s add Prettier and some plugins to make it work nicely with ESLint:</p>
<pre><code>yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
</code></pre>
<p><a href="https://github.com/prettier/eslint-config-prettier">eslint-config-prettier</a> will disable any linting rule that might interfere with an existing Prettier rule, and <a href="https://github.com/prettier/eslint-plugin-prettier">eslint-plugin-prettier</a> will run Prettier analysis as part of ESLint.</p>
<p>Let’s add them to our ESLint config to finish up our ESLint configuration. Make sure to put Prettier last so it can override other configs:</p>
<pre><code>{
  &quot;parser&quot;: &quot;@typescript-eslint/parser&quot;,
  &quot;extends&quot;: [
    &quot;plugin:@typescript-eslint/recommended&quot;,
    &quot;plugin:react/recommended&quot;,
    &quot;plugin:prettier/recommended&quot;,
    &quot;plugin:prettier/react&quot;,
    &quot;plugin:prettier/@typescript-eslint&quot;
  ],
  &quot;plugins&quot;: [
    &quot;@typescript-eslint&quot;,
    &quot;react&quot;,
    &quot;prettier&quot;
  ],
  &quot;rules&quot;: {
    &quot;react/react-in-jsx-scope&quot;: &quot;off&quot;
  },
  &quot;globals&quot;: {
    &quot;React&quot;: &quot;writable&quot;
  }
}
</code></pre>
<h3>Configure Prettier</h3>
<p>Prettier is opinionated and intentionally limits the number of options (<a href="https://prettier.io/docs/en/why-prettier.html">read why</a>). If you want, you can overwrite some rules of Prettier by creating a <code>.prettierrc</code> file in the root of your project:</p>
<pre><code>touch .prettierrc
</code></pre>
<p>The following configuration is an example file. I personally use this as my setting, but you can find all available configurations <a href="https://prettier.io/docs/en/configuration.html">in the official documentation</a>.</p>
<pre><code>{
	&quot;semi&quot;: true,
	&quot;singleQuote&quot;: true
}
</code></pre>
<h3>Add a git hook</h3>
<p>Optionally, you can use <a href="https://github.com/typicode/husky">husky</a> and <a href="https://github.com/azz/pretty-quick">pretty-quick</a> to add a git hook that will always lint and format your changed files.</p>
<p>First, install them as dev dependencies:</p>
<pre><code>yarn add pretty-quick husky -D
</code></pre>
<p>Now add the following <code>husky</code> and <code>lint-staged</code> configuration to your <code>package.json</code>:</p>
<pre><code>&quot;scripts&quot;: {
  ...
  &quot;lint&quot;: &quot;eslint --ext .ts,.tsx&quot;,
},
</code></pre>
<p>Add the pre-commit hook in your <code>package.json</code>:</p>
<pre><code>&quot;husky&quot;: {
  &quot;hooks&quot;: {
    &quot;pre-commit&quot;: &quot;pretty-quick --staged &amp;&amp; npm run lint&quot;
   }
},
</code></pre>
<p>Now, whenever you commit your changes, it will format and lint your new code.</p>
<hr/>
<h3>Code Editor Extensions</h3>
<p>If you haven’t done so already, I recommend installing the <a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier</a> and <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">ESLint</a> extensions for your code editor. If you don’t want to format your file manually every time, you can format it on save as well. I highly recommend this. For Visual Studio Code, all you need to do is open your VSCode User settings/preferences and set the <code>Format On Save</code> option to <code>true</code>.</p>
<p>That’s it! Thanks for reading. I hope it was helpful.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Set Up Next.js With TypeScript]]></title>
            <link>https://jakeprins.com/blog/how-to-set-up-next-js-with-typescript</link>
            <guid>https://jakeprins.com/blog/how-to-set-up-next-js-with-typescript</guid>
            <pubDate>Mon, 15 Jun 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[If you start a new React project, you might want to consider Next.js and TypeScript. In this article, I explain why this would be a great choice and walk you through the process of setting up a new project using these technologies.]]></description>
            <content:encoded><![CDATA[<p>If you start a new React project, you might want to consider <a href="https://nextjs.org/">Next.js</a> and <a href="https://www.typescriptlang.org/">TypeScript</a>. In this article, we explain why this would be a great choice and walk you through the process of setting up a new project using these technologies.</p>
<hr/>
<h2>Why Next.js</h2>
<p>There are many important details you need to consider when you start a new project with React. Your code has to be bundled using a bundler, like webpack, and transformed using a compiler, like Babel.</p>
<p>Create React App can be a nice tool to handle this for you and give you a massive head start, but what about code splitting, prerendering for performance, and SEO or server-side rendering?</p>
<p>To build a complete React application, you need more than CRA provides you with. You can save your self some time by using a Next.js, a React framework that provides a solution to all of these problems.</p>
<hr/>
<h2>Why TypeScript?</h2>
<p><a href="https://www.typescriptlang.org/">TypeScript</a> is an open-source language that builds on JavaScript by adding static type definitions. Types provide a way to describe the shape of an object, providing better documentation and allowing TypeScript to validate that your code is working correctly.</p>
<p>TypeScript seems to be taking over front-end development. Almost 90% of developers (who filled in <a href="https://2019.stateofjs.com/javascript-flavors/typescript/">the State of JS survey</a>) admit they’d like to use or learn to use TypeScript in their next project. Besides that, it’s one of the most loved languages, according to the 2019 <a href="https://insights.stackoverflow.com/survey/2019#most-loved-dreaded-and-wanted">developer survey</a> by Stack Overflow.</p>
<p>It’s very popular and has lots of benefits during development. I’ve specified these benefits some more in “<a href="https://codeburst.io/stack-choices-javascript-vs-typescript-c9db39b67bf6">Stack Choices: JavaScript vs TypeScript</a>.”</p>
<hr/>
<h2>Let’s Start</h2>
<p>If you start from scratch, open your terminal, and inside your projects folder, run <code>yarn create next-app</code> (Yarn is used in this example, but you could, of course, also go with npm).</p>
<p>You’ll get prompted with the following message:</p>
<pre><code>What is your project named?
</code></pre>
<p>Fill in a name, and hit enter. The next message you’ll see is:</p>
<pre><code>Pick a template
</code></pre>
<p>Choose <code>Default starter app</code>, and wait until your project is ready. Then, type <code>cd &lt;your-project-name&gt;</code> in your terminal to make sure you’re inside the directory where you can run <code>yarn dev</code> to start the development server.</p>
<p>If everything is fine, we can set up TypeScript. Start by creating an empty <code>tsconfig.json</code> file in the root of your project:</p>
<pre><code>touch tsconfig.json
</code></pre>
<p>Try restarting the development server (<code>yarn dev</code>). You’ll get a warning message, like this:</p>
<pre><code>It looks like you&#x27;re trying to use TypeScript but do not have the required package(s) installed.
</code></pre>
<p>Follow the instructions to install TypeScript:</p>
<pre><code>yarn add --dev typescript @types/react @types/node
</code></pre>
<p>Now, try starting the development server again. After starting the server, Next.js will:</p>
<ul>
<li>Populate the <code>tsconfig.json</code> file for you. You may customize this file.</li>
<li>Create the <code>next-env.d.ts</code> file, which ensures Next.js types are picked up by the TypeScript compiler. You should <em>not</em> touch this file.</li>
</ul>
<p>Now let’s try it out by changing the <code>index.js</code> to <code>index.tsx</code>. If nothing breaks, that means you can now use TypeScript for your Next.js application.</p>
<p>And that’s it! Good luck with your project, and feel free to ask any questions if you have any.</p>
<p>Thanks for reading.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[The Best Places to Post Your Startup]]></title>
            <link>https://jakeprins.com/blog/the-best-places-to-post-your-startup</link>
            <guid>https://jakeprins.com/blog/the-best-places-to-post-your-startup</guid>
            <pubDate>Fri, 22 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[A great way to show your product to the world is by sharing it on platforms like ProductHunt, Indiehackers, Reddit, etc. It’s also an easy way to get some traffic, feedback, and backlinks. Here are some great directories to submit your product.]]></description>
            <content:encoded><![CDATA[<p>A great way to show your product to the world is by sharing it on platforms like ProductHunt, IndieHackers, Reddit, etc. It’s also an easy way to get some traffic, feedback, and backlinks. Here are some great directories to submit your product.</p>
<h3>IndieHackers</h3>
<p>IndieHackers is a place where the founders of profitable businesses and side projects can share their stories transparently, and where entrepreneurs can come to read and learn from those examples.</p>
<p>Link: <a href="https://www.indiehackers.com/contribute?ref=makermove">https://www.indiehackers.com/contribute</a></p>
<h3>ProductHunt</h3>
<p>ProductHunt is now the forefront when it comes to showcasing your product to the startup directory world. It has a voting system that enables your product to rise in the list of new products. The benefits of Product Hunt if something goes viral, it can very quickly. It is a great platform to promote services on technology, games, or books, and well, pretty much everything.</p>
<p>Link: <a href="https://www.producthunt.com/?ref=makermove">https://www.producthunt.com/</a></p>
<h3>HackerNews</h3>
<p>If you have a technology-related startup and/or looking for some feedback, you can post it at HackerNews. Do not forget to mention that you are looking for feedback or give it an overly promotional title. Hacker News is a great platform if you have a startup serving a technical audience.</p>
<p>Link: <a href="https://news.ycombinator.com/?ref=makermove">https://news.ycombinator.com/</a></p>
<h3>MakerMove</h3>
<p><a href="https://www.makermove.com/">MakerMove</a> is a platform for startup founders and makers to discover useful resources &amp; tools. There are many curated lists of resources out there, but MakerMove has put them all together in a nice interface so you can easily discover cool products. It also has a full list of directories to post your startup.</p>
<p>Get inspired by the best books &amp; podcasts to help you grow. Find journalists to get press coverage or get connected with investors to fund your startup. Discover &amp; find anything you need to boost your project. MakerMove has it all in one place.</p>
<p>Link: <a href="https://www.makermove.com/">https://www.makermove.com/</a></p>
<h3>Startupbase.io</h3>
<p>Discover The Best New Startups Every Day. StartupBase is a community of makers and early adopters to share &amp; discuss the latest products and ideas. It is a place to discover and get early access to exciting new startups.</p>
<p>Link: <a href="https://startupbase.io/submit?ref=makermove">https://startupbase.io/submit</a></p>
<h3>BetaList</h3>
<p>BetaList is the place to discover and get early access to tomorrow’s startups. If you are fine giving finer details about your startup and product, it is worth registering and filling the format and BetaList.</p>
<p>Link: <a href="https://betalist.com/submit?ref=makermove">https://betalist.com/submit</a></p>
<h3>Inspire</h3>
<p>Explore, share, and get inspired by beautifully designed landing pages for mobile applications to design or improve your website.</p>
<p>Link: <a href="https://pageam.com/inspire/?ref=makermove">https://pageam.com/inspire/</a></p>
<h3>SideProjects</h3>
<p>Show off, give feedback and grow. SideProject.net is a relatively new platform that is perfect to submit your side project. It does not have a huge community behind it yet, but that can change.</p>
<p>Link: <a href="https://sideprojects.net/?ref=makermove">https://sideprojects.net/</a></p>
<h3>Owwly</h3>
<p>Find the best digital products &amp; learn from makers like you.</p>
<p>Link: <a href="https://owwly.com/?ref=makermove">https://owwly.com/</a></p>
<h3>Reddit-Entrepreneur</h3>
<p>Reddit-Entrepreneur is not a place to submit your startup, but you can tell stories for free about your product to of 100k+ subscribers. There is a likely chance with more discussion around your startup story or entrepreneurial concept, that you may get featured on the front page. The only limitation is that you need over 10 karma points, but if you’re an avid Reddit user, this is perfect to launch your startup in a startup directory.</p>
<p>Link: <a href="https://www.reddit.com/r/Entrepreneur/?ref=makermove">https://www.reddit.com/r/Entrepreneur/</a></p>
<h3>Uplabs</h3>
<p>UpLabs is the place to find high-quality design resources for designers, creative agencies and developers.</p>
<p>Link: <a href="https://www.uplabs.com/submit?ref=makermove">https://www.uplabs.com/submit</a></p>
<h3>All Top Startups</h3>
<p>Founded in February 2010, Alltopstartups has become a great resource site for young entrepreneurs starting and growing businesses. AllTopStartups is committed to sharing resources for entrepreneurs and startups, providing essential tools and resources to start, grow, and manage a startup.</p>
<p>Link: <a href="https://alltopstartups.com/submit-startup/?ref=makermove">https://alltopstartups.com/submit-startup/</a></p>
<h3>Inc42</h3>
<p>Inc42 is a leading Indian media and information platform, known for its end-to-end coverage of the Indian startup ecosystem.</p>
<p>Link: <a href="https://inc42.com/startup-submission/?ref=makermove">https://inc42.com/startup-submission/</a></p>
<h3>StartupBlink</h3>
<p>Have you ever wondered how the strength of your local startup ecosystem compares to others? What about the vital factors that help empower a growing ecosystem? StartupBlink’s comprehensive global startup ecosystem map and research center provides users with all the data they need to make better-informed business decisions.</p>
<h3>Stackshare</h3>
<p>Find the right developer tools and the companies that use them.</p>
<p>Link: <a href="https://stackshare.io/create-stack/new-company?ref=makermove">https://stackshare.io/create-stack/new-company</a></p>
<h3>Mashable</h3>
<p>Mashable often covers consumer-based startups directories (or anything which is of interest to its users) so it is worth submitting to it too here. If you get covered fine if not, that is OK. Do try to send them something newsworthy rather than just telling the features of the startup.</p>
<p>Link: <a href="https://mashable.com/submit/?ref=makermove">https://mashable.com/submit/</a></p>
<h3>Launching Next</h3>
<p>Very similar to BetaList, Launching Next is an impressive platform that features the most promising startups on a daily basis. Why do I mention impressive? It has great guides and content in relation to launching startups to gain early user traction and successful case studies of entrepreneurs launching startups at a low-cost. Further related to their content, they have individually interviewed startup founders launching their startups, and asking them their experiences of launching startups, and what lessons they learned.</p>
<p>Link: <a href="https://www.launchingnext.com/submit/?ref=makermove">https://www.launchingnext.com/submit/</a></p>
<p>Looking for more places? Check out the full list at <a href="https://www.makermove.com/directories"><strong>MakerMove</strong></a><strong>.</strong></p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Why I Migrated From Next.js to Create React App]]></title>
            <link>https://jakeprins.com/blog/why-i-migrated-from-next-js-to-create-react-app</link>
            <guid>https://jakeprins.com/blog/why-i-migrated-from-next-js-to-create-react-app</guid>
            <pubDate>Sat, 02 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Next.js is an amazing framework for React and has a lot more features that starting with Create React App. But for this project, I didn’t migrate from CRA to Next.js but the other way around — here’s why.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p><strong>Note:</strong> If you are looking to convert a Next.js application to React.js, check out my other article on <a href="https://www.jakeprins.com/blog/how-to-convert-nextjs-to-reactjs"><strong>how to convert Next.js to React.js</strong></a>.</p>
</blockquote>
<p>Let me start by saying I’m a big fan of <a href="https://nextjs.org/">Next.js</a>. It’s an amazing framework for building React applications, especially when you need Server-Side Rendering (SSR). One day, I’ll write an article covering all the great things about this framework, but luckily a lot of people are already doing that.</p>
<p>Instead, I’m now going to explain why I migrated one of my many side projects from Next.js to Create React App. Note: If you don’t care about the background story, scroll down and go straight to the conclusion.</p>
<h3>Start Free</h3>
<p>When you have many side projects (that don’t make you any money), it’s nice if you can host them for free. I have a Ruby on Rails project hosted on a custom domain on Heroku that’s already costing me more than 30 dollars a month, which is fine — if you have just one project or if that project is making you some money…
The cool thing about building a React application with Firebase and Firestore is that you can build a full-stack application and go live without any costs (except for the custom domain name). I did that with side projects like FlowReads and MakerMove. I also made React Milkshake, a boilerplate for creating apps with this stack. For a new side project, I was going for that same approach but with Nextjs, and although that’s working nicely, there was one issue I ran into.</p>
<h3>Deploying on Vercel</h3>
<p>The best place to deploy your Nextjs application is <a href="https://vercel.com/dashboard">Vercel</a> (formerly Zeit). Although you can start for free, I had to upgrade to the team plan (starting at 20 dollars a month) if I wanted to keep my current project live.
With Next.js, you can choose whether you want your pages to be statically generated or server-rendered. If your page is server-rendered, it means that a serverless function is created for your page during the build. The team at Zeit just decided to limit the number of serverless functions to 12 — and my application had 14 pages. That meant I could no longer deploy this application — it would fail during the build process due to this limitation. I can understand the decision to limit the number of serverless functions on the free tier, but the most frustrating part of this is that my site was already deployed. After Zeit has added this limitation I couldn’t deploy any changes without upgrading!
You might be thinking “But why are you server rendering every single page? Couldn’t you just statically serve at least a couple of pages?” That’s what Zeit also recommended and it’s exactly what I wanted. The only problem is that with my current implementation (using Redux), I can’t. Let me explain.</p>
<h3>SSR vs SSG</h3>
<p>With Next.js you can use getIntialProps (or since version 9.3 getServerSideProps and getStaticProps) inside your page components, which determines if a page should be server-rendered of statically generated (read more about that here). If you don&#x27;t need any data inside your page it will be statically generated, but when you use getInitialProps inside _app.js all your pages are server-rendered. You will get this warning message when building your application: &quot;You have opted-out of Automatic Static Optimization due to getInitialProps in pages/_app.&quot;
Now, when you use Redux as your state management library, you probably use next-redux-wrapper, so you don&#x27;t have to implement anything complex to make your client state of the store in sync with the server, etc. At least, that’s what I did, and here’s the problem: This library calls getInitialProps on your _app.js file, so all your pages will implement server-side rendering.
To be honest, my application didn’t need server rendering, because everything is behind a login screen. I basically only used Next.js because I wanted to experiment with it. I probably had more options than to just refactor my project to CRA, but I figured the hassle that can come from server-side rendering (even with Next.js) is not worth the time for a project that doesn’t even benefit from it that much. I also like to host things for free as long as I’m still in the development phase and with Next.js I’m not sure I can.</p>
<h3>Conclusion</h3>
<p>If you use redux inside your Nextjs application (with next-redux-wrapper) and have more than 12 routes, you will need to upgrade to the team plan if you host your project on Zeit.
If you don’t need SSR, are using redux, and also want to host it for free, you probably don’t want to go with Next.js. Also, if you want to use the SSG options of Next.js, you should not go with Redux. Otherwise, I can recommend you give Next.js a try because it’s a really good framework!</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Create React App vs Next.js]]></title>
            <link>https://jakeprins.com/blog/stack-choices-create-react-app-vs-next-js</link>
            <guid>https://jakeprins.com/blog/stack-choices-create-react-app-vs-next-js</guid>
            <pubDate>Mon, 20 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[CRA is a tool that gives you a massive head start when building React apps. It saves you from time-consuming setup and configuration. But for building performing production-ready applications, you might need a little bit more.]]></description>
            <content:encoded><![CDATA[<h1>Create React App vs Next.js</h1>
<p>As a developer, there are many choices to make when building your next application. The rise of serverless technologies allows developers to build and run applications without thinking about servers and this also allows front-end developers to create full-stack applications and build SAAS web apps.</p>
<p>In <strong>Stack Choices</strong>, we take a closer look at some technologies that can help us to build these types of applications and try to make a good decision on what to use. In the last episodes, I took a look at <a href="https://medium.com/stack-choices-javascript-vs-typescript-c9db39b67bf6">JavaScript vs TypeScript</a> and <a href="https://medium.com/stack-choices-react-vs-vue-vs-angular-vs-svelte-49aa0170c634">React vs Vue vs Angular vs Svelte</a>. This time, let’s take a look at <a href="https://reactjs.org/docs/create-a-new-react-app.html"><strong>Create React App</strong></a> <strong>(CRA) vs</strong> <a href="https://nextjs.org/"><strong>Next.js</strong></a>.</p>
<p><a href="https://reactjs.org/docs/create-a-new-react-app.html">CRA</a> is a tool that gives you a massive head start when building React apps. It saves you from time-consuming setup and configuration. You simply run one command and Create React App sets up the tools you need to start your React project. This is great to start focusing on building your application from the start and don’t waste time configuring your webpack etc. But for building performing production-ready applications, you might need a little bit more. You might need serverside-rendering for SEO, or code-splitting to increase performance. You could use a boilerplate that has some features built-in (like <a href="https://www.reactmilkshake.com/">React Milkshake</a>), or you can take a look at a framework like <a href="https://nextjs.org/">Next.js</a>.</p>
<h2>Next.js</h2>
<p>Next.js is a framework built by <a href="https://vercel.com/dashboard">Vercel</a>. It’s open-source, based on Node.js and Babel, and it integrates with React to develop single-page applications. It makes Serversside rendering very easy. According to <a href="https://nextjs.org/">nextjs.org</a>:</p>
<blockquote>
<p><em>With Next.js, server rendering React applications has never been easier, no matter where your data is coming from.</em></p>
</blockquote>
<p>Next.js also supports static exporting, pre-Rendering, and has a lot more nice features like automatic building size optimization, faster dev compilation, and preview mode.</p>
<p>In my opinion, the current version of Nextjs is what React was missing for a long time. It has all the features you need when you build a production-ready application that needs to perform. Also, the docs are nice and it’s becoming more and more popular among front-end developers.</p>
<p>Next.js is really good, but that doesn’t mean you should always go with this framework.</p>
<p>Although it makes server-side rendering a lot easier, it still introduces some complexity. For instance, if you use Redux, your store should also be in sync on the server. And when you use translated labels inside your application, be aware that this must also be handled on the server.</p>
<p>Also, for every server-rendered route that you have, a serverless function will be created. Currently, the free tier a limitation of 12 serverless functions, so if you use SSR and your app has more than 12 routes you need to pay for hosting. This might not be a problem for everybody, but when you build your app with CRA you are sure you can host it on platforms like <a href="https://vercel.com/dashboard">Vercel</a>, <a href="https://netlify.com/">Netlify</a>, or <a href="https://render.com/">Render</a> for free. I wrote an article that dives a little deeper into this, you can read it here: <a href="https://medium.com/better-programming/why-i-migrated-from-next-js-to-create-react-app-7e74834e8431">Why I Migrated From Next.js to Create React App</a></p>
<h2>Conclusion</h2>
<p>CRA is a great starting point for your next app, but to take things to the next level I think Nextjs can come in handy. It does introduce a little more complexity in certain areas and I’m not really happy with serverless functions limitations, but I think Next.js offers so many nice features that my decision is clear: <strong>Next.js</strong>.</p>
<p>If you think differently, please try to change my mind! Any other opinions? Leave them in the comments.</p>
<hr/>
<p>If you’re interested in saving time on your next project and skip implementing authentication, payments, etc. then <a href="https://serverless.page/">subscribe here</a> to follow my progress in building the SAAS starter-kit for JavaScript developers. You can also <a href="https://twitter.com/jake_prins">follow me on Twitter</a>, or at <a href="https://www.jakeprins.com/">www.jakeprins.com</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Stack Choices: SASS vs CSS-in-JS vs TailwindCSS]]></title>
            <link>https://jakeprins.com/blog/stack-choices-sass-vs-css-in-js-vs-tailwindcss</link>
            <guid>https://jakeprins.com/blog/stack-choices-sass-vs-css-in-js-vs-tailwindcss</guid>
            <pubDate>Wed, 19 Feb 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Let’s take a look at some options for styling the application. I’ve worked with different approaches like CSS modules, Styled-Components and frameworks like Bootstrap. In this post, we are going to focus on three of my favorites: SASS, CSS-in-JS, and Tailwind.]]></description>
            <content:encoded><![CDATA[<p>As a developer, there are many choices to make when building your next application. The rise of serverless technologies allows developers to build and run applications without thinking about servers and this also allows front-end developers to create full-stack applications and build SaaS web apps.</p>
<p>In <strong>Stack Choices</strong>, I take a closer look at some technologies that can help us to build these types of applications and try to make a good decision on what to use. In the last episodes, I took a look at <a href="https://medium.com/stack-choices-javascript-vs-typescript-c9db39b67bf6">JavaScript vs TypeScript</a>, <a href="https://medium.com/stack-choices-react-vs-vue-vs-angular-vs-svelte-49aa0170c634">React vs Vue vs Angular vs Svelte</a>, and <a href="https://medium.com/stack-choices-create-react-app-vs-next-js-f0f3c4db7083">Create React App vs Next.js</a>.</p>
<p>This time, let’s take a look at some options for styling the application. I’ve worked with different approaches like CSS modules, Styled-Components, frameworks like Bootstrap, or just vanilla CSS. In this post, we are going to focus on three of my favorites: SASS, CSS-in-JS, and Tailwind.</p>
<h2>SASS</h2>
<p>Going with classical CSS, using a preprocessor like SASS has been my way of styling applications for years. CSS is easy to learn but very hard to maintain. That’s why, as your project grows larger, you need a proper structure. <a href="http://getbem.com/">BEM</a> (Block Element Modifiers), is a naming styling that tries to solve the naming problem and structure that CSS often run into. BEM also provides a better structure for your CSS code and scalable CSS.</p>
<p>CSS preprocessors in combination with BEM definitely improve the development experience a lot, but that doesn’t mean it’s perfect. When your codebase grows, you might start to wonder in which files you should put certain styles and how it should be scoped. You also wonder if old styles are still being used. And you also have to think about performance: you don’t want to load in every single line of CSS on the first page when most of it is used on other pages.</p>
<p>There are many solutions to these problems, but one approach that I personally like is writing your CSS in JavaScript.</p>
<h2>CSS-in-JS</h2>
<p>There are multiple CSS-in-JS libraries out there, like <a href="https://styled-components.com/">Styled-Components</a>, <a href="https://emotion.sh/docs/introduction">Emotion</a>, and <a href="https://github.com/vercel/styled-jsx">Styled-JSX</a>, and it has some benefits over using classic CSS. I personally really like the fact that it makes it simple to add specific CSS to a component. All styles are only applied to the component, so this also means you do not have to learn methodologies like BEM, use long class names, or need to think about where you put your styles.</p>
<p>The other main advantage is that styles are rendered only if the component is on screen. You don’t have to worry about performance or unused styles. Also, because of the component-based architecture, we can have changes controlled by props. If you are a JavaScript developer I think you don’t need a lot of time to feel very familiar with writing your styles in JavaScript as well.</p>
<p>At first look, CSS-in-JS might look difficult, because the learning curve is a little bit bigger than using SASS. But if you are working on a component-heavy JavaScript project, I think CSS-in-JS is definitely worth trying out.</p>
<h2>TailwindCSS</h2>
<p><a href="https://tailwindcss.com/">Tailwind</a> is a CSS framework, but not like Bootstrap or Ant Design. It’s a utility-first framework, which means it doesn’t focus on predesigned components like buttons, cards, and alerts. It provides low-level utility classes that let you build completely custom designs without leaving your HTML.</p>
<p>If you go for a framework, you usually need to stick to the components of that framework, which can lead to overriding unwanted styles or battling specificity wars. Tailwind is different, it’s basically a different way of styling your application.</p>
<p>There are great benefits of using Tailwind, like development speed. Tailwind provides almost all the tools you need to build out a site in just HTML, so you rarely need to write any custom CSS.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>md:flex<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>md:flex-shrink-0<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>rounded-lg md:w-56<span class="token punctuation">&quot;</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>[https://cdn-images-1.medium.com/fit/c/32/32/1\*\_SiS1xvFOqiK6TnkiGcZ2A.jpeg](https://cdn-images-1.medium.com/fit/c/32/32/1*_SiS1xvFOqiK6TnkiGcZ2A.jpeg)<span class="token punctuation">&quot;</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>Jake<span class="token punctuation">&quot;</span></span><span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mt-4 md:mt-0 md:ml-6<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>text-sm tracking-wide uppercase text-cloud-burst-600 font- bold<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Marketing<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>#<span class="token punctuation">&quot;</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>block mt-1 text-lg font-semibold leading-tight text-gray-900 hover:underline<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Finding customers for your new business<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>mt-2 text-gray-600<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span>Getting a new business off the ground is a lot of hard work. Here are five ideas you can use to find your first customers.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre>
<p>Tailwind was built from the ground-up to be super customizable. It comes with a default configuration, but you can simply override the default configuration with a <code>tailwind.config.js</code> file in your project.</p>
<p>Also, any changes you make will be made in your templates, therefore only affecting the page you’re working on.</p>
<p>Besides that, all its utilities are also generated with responsive versions which are very helpful if you want to make your application look nice on mobile, tablet, and desktop.</p>
<p>One thing I didn’t like about using Tailwind is the long class names you can end up with. The more styling you need on a single element, the longer that class name is going to be. This harms the readability of your HTML files a bit, but after a while, I got used to it and it doesn’t bother me that much anymore.</p>
<p>The other problem with Tailwind is the large file size, but you can use <a href="https://github.com/FullHuman/purgecss">PurgeCSS</a> to fix this. PurgeCSS reduces the file size by scanning your HTML and removing any classes that aren’t used. Luckily, setting up PurgeCSS to work with Tailwind is easy, so this could make Tailwind an absolute winner.</p>
<h2>Conclusion</h2>
<p>There are many ways to manage the styling of your application. In the beginning, this is easy, but when your projects start to grow you will encounter difficulties. Using a traditional approach to CSS is not a wrong decision, but nowadays there are other options you could consider. Personally, I like CSS-in-JS a lot and as a JavaScript developer, it makes sense to me to go with this approach.</p>
<p>On the other hand, I’ve been working a lot with TailwindCSS lately and I don’t think I’ve ever been that excited about a CSS framework. I made some projects, like <a href="http://remoterocket.io">RemoteRocket.io</a>, with TailwindCSS and I’ve noticed a huge boost in productivity while working on it. Although it requires some time to learn all the utility classes, you barely need to write any CSS anymore once you master those. And not needing to switch files while styling your HTML elements feels like a blessing.</p>
<p>With all that in mind, I’ve made my stack choice: <strong>TailwindCSS</strong>. If you think differently, please try to change my mind! Reach out top me on Twitter: <a href="https://twitter.com/jake_prins">@jake_prins</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[React vs Vue vs Angular vs Svelte]]></title>
            <link>https://jakeprins.com/blog/stack-choices-react-vs-vue-vs-angular-vs-svelte</link>
            <guid>https://jakeprins.com/blog/stack-choices-react-vs-vue-vs-angular-vs-svelte</guid>
            <pubDate>Wed, 12 Feb 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[As a developer, there are many choices to make when building your next application. In Stack Choices, we take a closer look at some technologies to choose from and this time we take on the main JavaScript frameworks.]]></description>
            <content:encoded><![CDATA[<h1>React vs Vue vs Angular vs Svelte</h1>
<p>5 min read</p>
<p>As a developer, there are many choices to make when building your next application. The rise of serverless technologies allows developers to build and run applications without thinking about servers and this also allows front-end developers to create full-stack applications and <a href="https://serverless.page/">build SAAS web apps</a>.</p>
<p>In <strong>Stack Choices</strong>, we take a closer look at some technologies that can help us to build these types of applications and try to make a good decision on what to use. In the last episode, I took a look at <a href="https://medium.com/stack-choices-javascript-vs-typescript-c9db39b67bf6">JavaScript vs TypeScript</a>. This time, let’s take a look at the JavaScript frameworks.</p>
<h2>Angular</h2>
<p>This JavaScript framework, developed by Google, has been around for a while and it’s not really the cool kid on the block inside the frontend community. The move from angular 1 (AngularJS) to Angular 2, which was so different it required complete rewrites, seems to left some scars, but times have changed and lots of progress has been made.</p>
<p>Updating your application to the next major version of Angular has never been easier. Since Angular 8, you can just use <code>ng update</code> it to update your application and its dependencies. Besides that, with the new Angular CLI, you can generate components, routes, services, and pipes with a simple command. Besides that, Angular is very stable and has support for great technologies like Typescript and Web Workers, etc. Also, the new <strong>Ivy</strong> renderer is officially released in Angular 9, which decreased payload size and has a lot of other interesting benefits.</p>
<h3>My opinion</h3>
<p>After first working with React for quite some time I joined a team that worked with Angular. I have now worked with them for over a year and although I was a bit skeptical at the beginning, I started to like Angular more and more. Some people complain about the file size or that the framework is too bloated, but I think it’s nice to have a framework that has lots of features built-in. It’s not as easy to pick up as Vue or React, and I also wouldn&#x27;t choose it for smaller side-projects, but for production applications, especially those that you need to work on with multiple people, Angular is a very nice framework.</p>
<h2>React</h2>
<p>According to NPM trends, React is by far the most downloaded framework. Stack Overflow Trends also reveals that React receives the largest percentage. It’s easy to see why lots of people choose React for there new applications: it’s a save bet. Large companies such as Airbnb and Netflix have been using React.js for many years now, and we see that the technology is being embraced by more and more companies. The interest in React among developers is huge and that makes it a technology that will probably gain even more popularity in the upcoming years.</p>
<h3>My opinion</h3>
<p>Most popular doesn&#x27;t mean it’s the best, also React has its flaws. But personally, I absolutely like working with it. I have made most of my side-projects with React, like <a href="https://www.raterfox.com/">RaterFox</a>, <a href="https://codestash.co/">Codestash</a>, <a href="https://www.makermove.com/">MakerMove</a>, and more. I think it’s easy to get started with React, but when you build large applications you find yourself reaching for a lot of other libraries just to get some basic things done, like routing or global state management. These libraries usually all have there strong points and weaknesses. It’s nice to have the freedom to choose but at some point, I think it would help the developer community if we could just all go for the same thing. Instead of introducing yet another state management library or another way to implement CSS-in-JS, I think it might be beneficial to sometimes have fewer choices.</p>
<blockquote>
<p>I’ve made a React boilerplate called <a href="https://www.reactmilkshake.com/">React Milkshake</a> that can give you a headstart with your new project. And I’m working on a React starter-kit with Nextjs to build full-stack SaaS applications. You can <a href="https://serverless.page/">sign up here</a> to get notified when it launches.</p>
</blockquote>
<h2>Vue</h2>
<p>Another amazing JS framework is <a href="https://vuejs.org/">Vue.js</a>. With very detailed documentation and a fairly low learning curve, Vue.js is a suitable choice when you want to experiment with a new framework as a developer.</p>
<p>Because of the resemblance to React and Angular in terms of architecture, it is an easy transition from other frameworks. Loved for its small size, speed and flexibility, Vue.js delivers even <a href="https://www.stefankrause.net/js-frameworks-benchmark6/webdriver-ts-results/table.html">better performance</a> compared to other frontend frameworks.</p>
<p>Vue.js currently still has a much smaller market share compared to React. Due to the positive opinions from the developer community and the growth in the use of Vue, it seems likely that Vue.js will continue to develop positively.</p>
<h3>My opinion</h3>
<p>I have little personal experience with Vue, but to experiment with it I made <a href="https://remoterocket.io/">RemoteRocket</a>, a platform with all the remote jobs from popular remote job sites. It’s easy to pick up, especially with the Vue CLI, and I definitely recommend giving Vue a try (if you haven’t already).</p>
<h2>Svelte</h2>
<p>The big underdog of the frontend frameworks: Svelte. It’s been around for a couple of years and in April 2019 version 3.00 was released. Svelte is a little different than React, Vue, or Angular because it compiles your code to tiny, framework-less vanilla JS. And it’s not just very performant, with no run-time dependencies, it’s also very elegant and easy to learn the framework. It’s npm downloads have more than tripled over the last year, and in the results of the <a href="https://2019.stateofjs.com/front-end-frameworks/svelte/">2019 State of JS survey</a>, it showed that 44.9% of JS developers were interested in learning Svelte. Time has to tell if people will actually adopt it in production applications, so I’m curious about what the future will bring for Svelte.</p>
<h3>My opinion</h3>
<p>I personally haven’t got the time to try out Svelte, but it’s very high up on my list of technologies to explore. If you want to learn more I can recommend you <a href="https://www.youtube.com/watch?v=AdNJ3fydeao">this</a> video of Rich Harris’ (the creator of Svelte) talk about Rethinking reactivity or to listen to <a href="https://syntax.fm/show/250/scott-teaches-wes-svelte-and-sapper">this</a> podcast episode of the <a href="http://syntax.fm">Syntax.fm</a> where Scott Tolinski teaches Wes Bos about Svelte. It got me really excited and I can’t wait to start trying it out myself!</p>
<h1>Conclusion</h1>
<p>Angular, React, Vue, or Svelte, these are all great frameworks. I’ve worked with all of them except Svelte. I would really love to work with Svelte, but for my plan to build <a href="https://serverless.page/">the best JavaScript boilerplate</a> for building a SAAS business, I need to go with a framework I’m very experienced with. Besides that, popularity plays an important role, and for that reason, my choice has become: <strong>React</strong>.</p>
<p>If you think differently, please try to change my mind! Any other opinions? Leave them in the comments.</p>
<hr/>
<p>If you’re interested in saving time on your next project and skipping implementing authentication, payments, tests, etc. then <a href="https://serverless.page/">subscribe here</a> to follow my progress in building the SAAS starter-kit for JavaScript developers. You can also <a href="https://twitter.com/jake_prins">follow me on Twitter</a>, or at <a href="https://www.jakeprins.com/">www.jakeprins.com</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Code Splitting by Routes and Components]]></title>
            <link>https://jakeprins.com/blog/code-splitting-by-routes-and-components</link>
            <guid>https://jakeprins.com/blog/code-splitting-by-routes-and-components</guid>
            <pubDate>Wed, 22 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[When your app's bundle starts to grow it will slow things down. That's why we see a lot more use of code-splitting in modern web development. This is a simple tutorial on how to implement code-splitting in React.]]></description>
            <content:encoded><![CDATA[<p>When your app&#x27;s bundle starts to grow it will slow things down. That&#x27;s why we see a lot more use of code-splitting in modern web development. Code-splitting is the process of taking one large bundle containing your entire app, and splitting them up into multiple smaller bundles which contain separate parts of your app. This technique allows you to load chunks of code only when needed.</p>
<p>For example, when a visitor enters your application on the homepage, there is no need to load in all the code related to a completely separate page. That user might not even go to that route at all, so we only want to load it when the user navigates to that page. If we can load only the code necessary for the home page this means our initial loading time will be al lot faster, especially on slow networks.</p>
<p>In this post we will take a look at how we can boost the performance of our React applications by implementing code-splitting using <a href="https://github.com/jamiebuilds/react-loadable">React Loadable</a>. If you rather save time and start with a boilerplate that includes code-splitting, try out <a href="https://www.reactmilkshake.com/">React Milkshake</a>.</p>
<h3>Route-based splitting</h3>
<p>A great way to get started is to implement route-based code splitting, which means we load code chucks according to the current route.</p>
<p>Normally, our routes could something look like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Route</span><span class="token punctuation">,</span> <span class="token maybe-class-name">Switch</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-router-dom&#x27;</span>

<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Home</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;pages/Home&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Example</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;pages/Example&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">Routes</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Switch</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/<span class="token punctuation">&quot;</span></span> <span class="token attr-name">exact</span> <span class="token attr-name">component</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token maybe-class-name">Home</span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/example<span class="token punctuation">&quot;</span></span> <span class="token attr-name">component</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token maybe-class-name">Example</span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Switch</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Routes</span>
</code></pre>
<p>Now, let&#x27;s refactor these route to implement code splitting using React Loadable. The <code>Loadable</code> higher-order component takes an object with two keys: <code>loader</code> and <code>loading</code>.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Route</span><span class="token punctuation">,</span> <span class="token maybe-class-name">Switch</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-router-dom&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Loadable</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-loadable&#x27;</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">AsyncHome</span> <span class="token operator">=</span> <span class="token function"><span class="token maybe-class-name">Loadable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">loader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token string">&#x27;./pages/Home&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">AsyncExample</span> <span class="token operator">=</span> <span class="token function"><span class="token maybe-class-name">Loadable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">loader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token string">&#x27;./pages/Example&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">Routes</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Switch</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/<span class="token punctuation">&quot;</span></span> <span class="token attr-name">exact</span> <span class="token attr-name">component</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token maybe-class-name">AsyncHome</span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Route</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/example<span class="token punctuation">&quot;</span></span> <span class="token attr-name">component</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token maybe-class-name">AsyncExample</span><span class="token punctuation">}</span></span> <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Switch</span></span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Routes</span>
</code></pre>
<p>With this simple setup, the code related to the <code>Example</code> component will only load when that route is active. If you open your inspector in your browser and go to your network tab (js), you can see that if you change your routes a new code chunk will be loaded.</p>
<p>Pro tip. If you want to give your chunk a name instead of a generated hash, so you can clearly see which chunk just loaded, you can set the <code>webpackChunkName</code> like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token maybe-class-name">AsyncExample</span> <span class="token operator">=</span> <span class="token function"><span class="token maybe-class-name">Loadable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">loader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;Example&quot; */</span> <span class="token string">&#x27;./pages/Example&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>Sometimes components load really quickly ( &lt; 200ms) and the loading screen only quickly flashes on the screen. A number of user studies have proven that this causes users to perceive things taking longer than they really have. If you don&#x27;t show anything, users perceive it as being faster. Thankfully, your loading component will also get a pastDelay prop which will only be true once the component has taken longer to load than a set delay. Be default, delay is set to 200ms.</p>
<p>To do that, let&#x27;s create a <code>Loader</code> component that we can use in our sample component that will now look like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword">const</span> <span class="token maybe-class-name">AsyncExample</span> <span class="token operator">=</span> <span class="token function"><span class="token maybe-class-name">Loadable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">loader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: &quot;Example&quot; */</span> <span class="token string">&#x27;./pages/Example&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token maybe-class-name">Loader</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<p>And our <code>Loader</code> component:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">Loader</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>props<span class="token punctuation">.</span><span class="token property-access">pastDelay</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token keyword null nil">null</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Loader</span>
</code></pre>
<p>But what if something goes wrong while loading the code? Well, luckily React Loadable also provides use with an <code>error</code> prop. This means our final <code>Loader</code> component will look like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">Loader</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> pastDelay<span class="token punctuation">,</span> error <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Sorry, there was a problem loading the page.</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>pastDelay<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Loading...</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
    <span class="token keyword control-flow">return</span> <span class="token keyword null nil">null</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">Loader</span>
</code></pre>
<p>And that&#x27;s it!</p>
<h3>Load on hover</h3>
<p>Now we can even go a little further. We can also start loading the next chunk as soon as the user starts to hover over the link. To achieve this, all we have to do is call <code>preload()</code> on our Loadable component. It will look something like this:</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">Link</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-router-dom&#x27;</span>

<span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> <span class="token maybe-class-name">AsyncExample</span> <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;routes&#x27;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">SideBar</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>sidebar<span class="token punctuation">&quot;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/<span class="token punctuation">&quot;</span></span> <span class="token attr-name">exact</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        Home
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">Link</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">&quot;</span>/example<span class="token punctuation">&quot;</span></span> <span class="token attr-name">onMouseOver</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token maybe-class-name">AsyncExample</span><span class="token punctuation">.</span><span class="token method function property-access">preload</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        Example
      </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token class-name">Link</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
    </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">SideBar</span>
</code></pre>
<p>And that&#x27;s it, awesome!</p>
<h3>Component-based splitting</h3>
<p>Now that we now how to code split based on the current route, let&#x27;s take it even a little further and look at how we can code split on component level. Inside your container component you might render different components based on a certain state, like if an user is logged in or not. We can achieve this with the same Loadable component. Take a look at this example, in which a component is only rendered into the view once the user clicks on the button.</p>
<pre class="language-jsx"><code class="language-jsx"><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">React</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react&#x27;</span><span class="token punctuation">;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Loadable</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;react-loadable&#x27;</span><span class="token punctuation">;</span>
<span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Loader</span></span> <span class="token keyword module">from</span> <span class="token string">&#x27;components/Loader&#x27;</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token maybe-class-name">SomeComponent</span> <span class="token operator">=</span> <span class="token function"><span class="token maybe-class-name">Loadable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  <span class="token function-variable function">loader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token keyword module">import</span><span class="token punctuation">(</span><span class="token string">&#x27;components/SomeComponent&#x27;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token maybe-class-name">Loading</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">App</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>showComponent<span class="token punctuation">,</span> setShowComponent<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
    <span class="token keyword control-flow">if</span> <span class="token punctuation">(</span>showComponent<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token class-name">SomeComponent</span></span> <span class="token punctuation">/&gt;</span></span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword control-flow">else</span> <span class="token punctuation">{</span>
      <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Hello! 👋</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
          </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=&gt;</span> <span class="token function">setShowComponent</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token plain-text">Click me!</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span><span class="token plain-text">
        </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token maybe-class-name">App</span><span class="token punctuation">;</span>
</code></pre>
<p>Obviously with such a simple component it doesn’t make a difference, but with larger components in an app it can be a good idea to implement code-splitting on component-level like this.</p>
<p>And with this, you should be ready to implement code splitting in your React apps! Checkout the repo of <a href="https://github.com/jamiebuilds/react-loadable">React Loadable</a> for more options. If you are looking for a nice boilerplate that comes with code-splitting out of the box, try out <a href="https://www.reactmilkshake.com/">React Milkshake</a>.</p>
<p>Thanks for reading! If you want to ben notified when I release new projects or articles then follow me on twitter: <a href="https://twitter.com/jake_prins">@jake_prins</a>.When your app&#x27;s bundle starts to grow it will slow things down. That&#x27;s why we see a lot more use of code-splitting in modern web development. Code-splitting is the process of taking one large bundle containing your entire app, and splitting them up into multiple smaller bundles which contain separate parts of your app. This technique allows you to load chunks of code only when needed.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Early Access to the Serverless SaaS Boilerplate]]></title>
            <link>https://jakeprins.com/blog/early-access-to-the-serverless-saas-boilerplate</link>
            <guid>https://jakeprins.com/blog/early-access-to-the-serverless-saas-boilerplate</guid>
            <pubDate>Sun, 05 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Serverless SaaS, a boilerplate that will give you the perfect starting point for your next React app to build SaaS applications, is going in Early Access. Save time and buy a head start for your next project.]]></description>
            <content:encoded><![CDATA[<p>Finally, <a href="https://serverless.page/">Serverless SaaS</a> goes live! 🎉</p>
<p>Serverless SaaS is a boilerplate that will give you a great starting point for your next React app to build SaaS applications. For the last couple of months, I have been working hard to implement features like authentication, billing, teams, and a lot more. And now it&#x27;s time to start early access.</p>
<p>If you are interested you can be one of the first to work with this starter-kit and save lots of time building your next (SaaS) application. In the upcoming days, you can still get it with a 50% discount!</p>
<p>Just visit <a href="https://serverless.page/">serverless.page</a> to find all the information you need. If you have any questions or feedback please let me know.</p>
<p>You can reach out to me on Twitter: <a href="https://twitter.com/jake_prins">@jake_prins</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Stack Choices: JavaScript vs TypeScript]]></title>
            <link>https://jakeprins.com/blog/stack-choices-javascript-vs-typescript</link>
            <guid>https://jakeprins.com/blog/stack-choices-javascript-vs-typescript</guid>
            <pubDate>Thu, 19 Dec 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[In Stack Choices, we take a closer look at some technologies that can help us to build great applications and try to make a good decision on what to use. Let’s take a look at going with plain old JavaScript or the rising star: TypeScript.]]></description>
            <content:encoded><![CDATA[<p>As a developer, there are many choices to make when building your next application. The rise of serverless technologies allows developers to build and run applications without thinking about servers and this also allows front-end developers to create full-stack applications and build SAAS web apps.</p>
<p>In <strong>Stack Choices</strong>, we take a closer look at some technologies that can help us to build these types of applications and try to make a good decision on what to use. This time, let’s take a look at going with plain old JavaScript or the rising star: <a href="https://www.typescriptlang.org/">TypeScript</a>.</p>
<h2>What is TypeScript?</h2>
<p>TypeScript is an open-source language that builds on JavaScript by adding static type definitions. Types provide a way to describe the shape of an object, providing better documentation, and allowing TypeScript to validate that your code is working correctly.</p>
<p>You can easily use Typescript when starting a new React application by running <code>yarn create react-app my-app --template typescript</code> or work with Angular, which comes with Typescript by default.</p>
<h2>How popular is TypeScript?</h2>
<p>TypeScript seems to be taking over front-end development. Almost 90% of developers (who filled in <a href="https://2019.stateofjs.com/javascript-flavors/typescript/">the state of js survey</a>) admit they would like to use or learn TypeScript in their next project. Besides that, it&#x27;s one of the most loved languages, according to the 2019 <a href="https://insights.stackoverflow.com/survey/2019#most-loved-dreaded-and-wanted">Developer Survey</a> by Stack Overflow.</p>
<h2>My personal experience</h2>
<p>I have been working with Typescript full-time for over a year now, and I have experienced the benefits of using TypeScript when working in a team. Something I personally like is, for example, the rich IDE support. I’m using <a href="https://code.visualstudio.com/">VSCode</a> and it offers features like code navigation, autocompletion, and providing accurate suggestions. You also get feedback while typing like flagging type-related errors as soon as they occur. This can help you write more maintainable code.</p>
<p>Although TypeScript is really good, it’s not perfect. In the beginning, I didn’t like working with TypeScript at all. It requires some time to learn and you need to write more code than with just JavaScript.</p>
<p>Here are, in my opinion, the pros and cons of choosing Typescript for your next project.</p>
<h3>Pros for using Typescript</h3>
<ul>
<li>It has rich IDE support</li>
<li>It has object-oriented programming features</li>
<li>It helps to spot bugs early</li>
<li>It is used in popular frameworks</li>
<li>It’s growing in popularity</li>
</ul>
<h3>Cons for using Typescript</h3>
<ul>
<li>Writing more code</li>
<li>Takes time to learn</li>
<li>Adds complexity</li>
</ul>
<h2>Conclusion</h2>
<p>Although TypeScript requires you to write some more code and could take developers some time to learn, it’s benefits outweigh that in my opinion. We also see steady growth in the adoption of the language so I think this is not a hard decision to make. My stack choice: TypeScript.</p>
<p>If you think differently, please try to change my mind! Any other opinions? Leave them in the comments.</p>
<hr/>
<p>If you’re interested in saving time on your next project and skipping implementing authentication, payments, tests, etc. then <a href="https://serverless.page/">subscribe here</a> to follow my progress in building the SAAS starter-kit for JavaScript developers. You can also <a href="https://twitter.com/jake_prins">follow me on Twitter</a>, or at <a href="https://www.jakeprins.com/">www.jakeprins.com</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Interesting frontend technologies to follow]]></title>
            <link>https://jakeprins.com/blog/interesting-frontend-technologies-to-follow</link>
            <guid>https://jakeprins.com/blog/interesting-frontend-technologies-to-follow</guid>
            <pubDate>Fri, 02 Aug 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[The world of web development is constantly changing and it is a challenge to keep a close eye on everything that is going on. Let’s discuss a couple of interesting frontend technologies for web development.]]></description>
            <content:encoded><![CDATA[<h1>Interesting frontend technologies to follow</h1>
<p>The world of web development is constantly changing and it is a challenge to keep a close eye on everything that is going on. Which frameworks will still be relevant in the coming year? And which tools are most suitable for your web application? Let’s discuss a couple of interesting frontend technologies for web development.</p>
<h2>Svelte</h2>
<p>The big underdog of the frontend frameworks: Svelte. It’s been around for a couple of years and in April 2019 version 3.00 was released. Svelte is a little different than React, Vue or Angular because it compiles your code to tiny, framework-less vanilla JS. And it’s not just very performant, with no run-time dependencies, it’s also very elegant and easy to learn the framework. It’s npm downloads have more than tripled over the last year, and in the results of the <a href="https://2019.stateofjs.com/front-end-frameworks/svelte/">2019 State of JS survey</a>, it showed that 44.9% of JS developers were interested in learning Svelte. Time has to tell if people will actually adopt it in production applications, so I’m curious about what the future will bring for Svelte.</p>
<h2>Expo &amp; React Native</h2>
<p><a href="https://facebook.github.io/react-native/">React Native</a>, the framework for building native mobile apps with React has had a steady growth in the last couple of years. The <a href="https://2019.stateofjs.com/mobile-desktop/reactnative/">2019 State of JS survey</a> from 2019 showed that last year 27.9% had used React Native and would use it again, together with 44.3% that was interested in learning React Native. I have been using React Native to build the mobile apps of <a href="https://get.raterfox.com/">RaterFox</a>, and really enjoyed it. It was easy to pick up because I already built <a href="https://www.raterfox.com/">the frontend of RaterFox</a> with React. One thing that&#x27;s kind of annoying right now, is that I’m managing 2 codebases that really look alike. Especially all Redux code is mostly the same for the website as the mobile app. Luckily for me, the React Native teams have even bigger plans than just building for Android and IOS with one codebase. What about building for mobile, web and desktop, all with React Native? Sounds too good to be true, but it is very real. Expo, an open-source platform for making React Native apps, is on a mission to create the best possible end-to-end developer experiences for building and deploying apps on multiple platforms. I would definitely keep an eye out for Expo and highly recommend this talk from Evan Bacon about Expo for mobile, web, desktop and console: <a href="https://www.youtube.com/watch?v=ykBxY01j_rA">https://www.youtube.com/watch?v=ykBxY01j_rA</a></p>
<p>Btw, if you are looking for a nice boilerplate for your next React projects, check out <a href="https://www.reactmilkshake.com/">React Milkshake</a>.</p>
<h2>TypeScript</h2>
<p>TypeScript seems to be taking over frontend development. Almost 90% of developers (who filled in <a href="https://2019.stateofjs.com/javascript-flavors/typescript/">the state of js survey</a>) admit they would like to use or learn TypeScript in their next project.</p>
<p>TypeScript is an open-source language that builds on JavaScript by adding static type definitions. Types provide a way to describe the shape of an object, providing better documentation, and allowing TypeScript to validate that your code is working correctly.</p>
<p>Migrating an existing project from Javascript to Typescript is probably a huge task and maybe not worth it, but you should definitely try out Typescript in your new projects (if you aren’t already). You can easily use Typescript when starting a new React application by running <code>yarn create react-app my-app --template typescript</code> or work with Angular, which comes with Typescript by default.</p>
<h2>Angular</h2>
<p>Speaking of Angular, let’s talk about the great things of this framework. Yes, <a href="https://2019.stateofjs.com/front-end-frameworks/react/">React is eating the world</a>, and Vue has definitely proved itself in the last couple of years, but while everybody seems to be talking about the shiny new tools we might forget to pay a little attention to Angular. The JavaScript framework, developed by Google, deserves a little more love, even though it doesn’t have the best reputation inside the frontend developer community. The move from angular 1 (AngularJS) to Angular 2, which was so different it required complete rewrites, left some big scars. Times have changed though, and lots of progress has been made.</p>
<p>Updating your application to the next major version of Angular has never been easier. Since Angular 8, you can just use <code>ng update</code> to update your application and its dependencies. Besides that, with the new Angular CLI you can generate components, routes, services and pipes with a simple command. Besides that, Angular is very stable and has support for great technologies like Typescript and Web Workers, etc., the new <strong>Ivy</strong> renderer is officially released in Angular 9. Although the framework has made huge improvements in file size and runtime speed since the days of Angular 2, Angular apps often tend to be on the heavy side when it comes to file size and memory use. Ivy aims to change this. Compared with the current Angular View Engine, Ivy provides the following benefits:</p>
<ul>
<li>Rebuild times are significantly faster</li>
<li>Decreased payload size</li>
<li>Better template type checking, so you can catch more errors at build time</li>
<li>The code generated by the Angular compiler is now much easier for humans to read and understand</li>
</ul>
<p>And it’s broadly compatible with existing Angular applications. It’s also great to know that Angular is so wildly used inside Google, and it’s comforting to know that these updates have already been released to production on dozens of sites across Google before it gets to the public.</p>
<p>Also pretty cool is <a href="https://twitter.com/scullyio">@Scullyio</a>, the static site generator for Angular. It’s still in beta but it looks very promising!</p>
<h2>Web components</h2>
<p>Pure web components are framework agnostic and can work without a framework. That’s why a lot of people think that web components are the future. They are free from JS fatigue and are supported by modern browsers.</p>
<p>Prominent tools to know in this space are <a href="https://stenciljs.com/">StencilJS</a>, <a href="https://lit-html.polymer-project.org/">Lit-HTML</a> (and Lit-element) and <a href="https://github.com/skatejs/skatejs">SkateJS</a>. But you can also use Angular or Svelte to create web components.</p>
<p>Although the idea of web components sound very promising, the reality shows that there are also some reasons why you should be careful about fully betting on web components. There are many articles were written about the subject, but when the creator of Svelte posted <a href="https://dev.to/richharris/why-i-don-t-use-web-components-2cia">Why I don’t use web components</a> it resulted in some interesting discussions.</p>
<p>Personally I’m not (yet) completely convinced about the idea of web components, but it’s for sure a technology that I keep on my radar.</p>
<p>There are many more interesting topics I would like to discuss, but let’s wrap things up here for now. Add some of your great tools for frontend development in the comments and follow me on twitter <a href="https://twitter.com/jake_prins">@jake_prins</a> to stay up to date about my latest work.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Interesting Web Technologies to follow in 2019]]></title>
            <link>https://jakeprins.com/blog/interesting-web-technologies-to-follow-in-2019</link>
            <guid>https://jakeprins.com/blog/interesting-web-technologies-to-follow-in-2019</guid>
            <pubDate>Wed, 09 Jan 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[The world of web development is constantly changing and it is a challenge to keep a close eye on everything that is going on. Which frameworks will still be relevant in the coming year? And which programming language is most suitable for your web application? Let’s discuss six interesting technologies for web development.]]></description>
            <content:encoded><![CDATA[<p>The world of web development is constantly changing and it is a challenge to keep a close eye on everything that is going on. Which frameworks will still be relevant in the coming year? And which programming language is most suitable for your web application? Let’s discuss six interesting technologies for web development.</p>
<p>In this article, we distinguish between front-end and back-end technology. We start with three front-end development frameworks and then discuss three back-end development frameworks.</p>
<h2>Front-end Frameworks</h2>
<h3>1. React.js</h3>
<p>With <a href="https://reactjs.org/">React.js</a>, developers can build reusable components, each with their own “state”. If one component has to be updated on the page (for example the user clicks a like-button), only that one part is modified by React (the thumb goes up) without having to refresh the complete page.</p>
<p>React uses a ‘virtual DOM’ for this. This is a representation of a user interface that is stored in the memory and is constantly synchronized with the “real” DOM. It does not matter which browser you use, the virtual DOM of React will always render faster because it works in a more efficient way.</p>
<p>Large companies such as Airbnb and Netflix have been using React.js for a number of years now, and we see that the technology is being embraced by more and more companies. The interest in React among developers is huge and that makes it a technology that will only gain popularity in the coming years.</p>
<p>In addition to React there is also <a href="https://facebook.github.io/react-native/">React Native</a>, a framework that makes it possible to develop mobile apps for both iOS and Android with only one set of source code. The framework has the same technical basis as React and with that, it has suddenly become much easier for web developers to develop mobile apps in addition to websites and web applications and this has been an interesting development for companies.</p>
<h3>2. Vue.js</h3>
<p>Another important newcomer in front-end development is <a href="https://vuejs.org/">Vue.js</a>, the Javascript framework with the fastest growing and perhaps the most enthusiastic developer community at the moment. With very detailed documentation and a fairly low learning curve, Vue.js is a suitable choice when you want to experiment with a new framework as a developer.</p>
<p>Because of the resemblance to React and Angular in terms of architecture, it is an easy transition from other frameworks. Loved for its small size (only 20KB), speed and flexibility, Vue.js delivers even <a href="https://www.stefankrause.net/js-frameworks-benchmark6/webdriver-ts-results/table.html">better performance</a> compared to other frontend frameworks.</p>
<p>Vue.js currently still has a fairly small market share compared to React or Angular. Finding developers with experience in Vue.js can, therefore, be a little more difficult. Whether Vue will become as popular as React or Angular we’ll have to wait and see, but due to the positive opinions from the developer community and the growth in the use of Vue, it seems likely that Vue.js will continue to develop positively.</p>
<h3>3. jQuery</h3>
<p>But what about <a href="https://jquery.com/">jQuery</a>? Well, it seems that the developer community is slowly moving away from the most famous JavaScript library. One of the reasons may be that jQuery does not go well with newer libraries like React. Modern browsers nowadays also support many jQuery-like functions, which in many cases makes jQuery superfluous. For that reason, we see that a lot more use of pure JavaScript. For example, <a href="https://github.com/">GitHub</a> recently <a href="https://githubengineering.com/removing-jquery-from-github-frontend/">announced</a> that it had replaced all jQuery with pure JavaScript.</p>
<p>jQuery may not always be the first choice when starting a new project, with 300,000 downloads per day it still achieved a good 300% growth compared to the beginning of 2017. Perhaps even more surprising: 90% of the top 500,000 websites still make use of jQuery! This shows that a library does not necessarily have to be new and cool to remain relevant. The choice to use a particular library depends entirely on the type of project which you want to use it for.</p>
<p>The changes in front-end web development are occurring incredibly fast. It seems impossible to follow everything, but by monitoring and understanding the most important frameworks, we can gain an insight and make better choices at the start of a new project.</p>
<p>Each of the frameworks/libraries has a different philosophy and each has its own strength, but one thing we can be certain of is that JavaScript is the basis of all these developments. Therefore, a development team that has strong knowledge and experience with JavaScript is essential for building modern websites and web applications.</p>
<h2>Backend-Frameworks</h2>
<h3>4. Kotlin</h3>
<p>A programming language that has grown enormously in popularity in recent years is <a href="https://kotlinlang.org/">Kotlin</a>. It’s a modern alternative to Java that is easy to learn. The open source language started to grow especially last year when Google announced that it was an officially supported language for Android. Many companies started writing their Android apps with Kotlin, such as Slack and Netflix. Rob Fletcher, a senior software developer at Netflix, wrote:</p>
<blockquote>
<p>“Kotlin is not revolutionary, but it feels like a very careful merging of some of the best features of other languages.”</p>
</blockquote>
<p>It is interesting to see, as <a href="https://pusher.com/state-of-kotlin">a research by Pusher</a> showed, that Kotlin was used mainly by experienced developers in the first years (since 2011), but since the announcement of Google, it’s the young developers, especially students, who are causing the enormous growth. In addition, it is not entirely surprising that most of the developers now use Kotlin to build Android apps, even though it can also be used for developing back-end or server-side applications.</p>
<h3>5. Elixir and Phoenix</h3>
<p><a href="https://phoenixframework.org/">Phoenix</a>, built in the functional programming language <a href="https://elixir-lang.org/">Elixir</a>, is a relatively new framework that is increasingly being talked about in the developer community. One of the reasons why developers are so interested in Phoenix is ​​because it is known for its fast performance. For example, processing a large JSON request, which takes about 1.5 seconds in Rails, only costs 400ms in Phoenix.</p>
<p>It remains important to check whether a technology is suitable for the type of project in which you want to apply it. For example, Elixir and Phoenix are strong in real-time applications, in which many connections have to be processed simultaneously (for example chat applications). But for applications that require a lot of computing power, Elixir could be less suitable.</p>
<p>The number of Elixir developers is currently quite limited. Elixir and Phoenix are relatively new and there are not many companies that work openly with the framework. This can certainly change in the future and that is why you should keep a close eye on Elixir and Phoenix.</p>
<h3>6. GraphQL</h3>
<p>You may have heard of <a href="https://graphql.org/">GraphQL</a>, an open-source query language developed by Facebook, and one of the most modern ways to build APIs. The popularity of GraphQL is growing rapidly and large companies such as Twitter, Shopify and Amazon have already started working with the technology.</p>
<p>GraphQL offers solutions for some very real problems that developers wrestle with a lot because it makes loading and processing data much easier. One of the important differences between a REST API and GraphQL, is that GraphQL offers the possibility to specify exactly which data you want to get back from the API. It ensures that the software calling the API receives all the data it needs in a single request.</p>
<p>This comes in handy when the application contains many nested models and associations. GraphQL helps, for example when developing mobile apps, to only load the relevant data and thus reduce the load on the mobile network. GraphQL takes care of a lot of the overhead that REST APIs have and that delivers benefits to both the backend and frontend.</p>
<h2>Conclusion</h2>
<p>As a developer, it is important to stay up to date with current technological developments as much as possible. You should look at the opportunities, but also consider the possible risks that new technology entails. Experimenting regularly with different frameworks helps to experience the potential benefits, but also the limitations.</p>
<p>The work of a developer consists mainly of solving technical challenges and that is why it is of great importance to work with proven technology, which is productive to work with and has a large community behind it.</p>
<p>Thanks for reading! Will write a new post with 6 other technologies to follow in 2020. Got suggestions?</p>
<p>// @NOTE: You can find me on Twitter <a href="https://twitter.com/jake_prins">@jake_prins</a>.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Here's what I learned at the world's biggest React conference]]></title>
            <link>https://jakeprins.com/blog/here-is-what-i-learned-at-the-worlds-biggest-react-conference</link>
            <guid>https://jakeprins.com/blog/here-is-what-i-learned-at-the-worlds-biggest-react-conference</guid>
            <pubDate>Tue, 24 Apr 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[On Friday, April 13th, I attended the biggest React conference in the world: React Amsterdam. Together with 1200 frontend and full-stack developers from across the globe, we gathered in the tech heart of Europe for an event with over 25 speakers. Let me tell you five things I learned on a day filled with great talks, lots of coffee, and of course some beers.]]></description>
            <content:encoded><![CDATA[<p>On Friday, April 13th (2018), three colleagues and I from <a href="https://www.inspire.nl/">Inspire</a> attended the biggest React conference in the world:<a href="https://react.amsterdam/">React Amsterdam</a>. Together with 1200 frontend and full stack developers from across the globe, we gathered in the tech heart of Europe for an event with over 25 speakers.</p>
<p>Let me tell you five things I learned on a day filled with great talks, lots of coffee, and of course some beers.</p>
<h2>1. Reactive programming</h2>
<p>There will always be a new JavaScript framework to learn. Technology will continue to evolve and change, and developers will continue to rewrite applications. <em><a href="https://twitter.com/ladyleet">Tracy Lee</a> talked about reactive programming, which can enable you to just copy-paste most of your code from framework to framework.</em></p>
<p>In her talk, Tracy showed why reactive programming can be a more efficient way to code. She also discussed how it has been adopted by industry leaders such as Netflix, Slack, Microsoft, and Facebook as the new standard for developing applications. It seems very promising, especially libraries like RxJS, that help developers deliver complex features quicker with less, more maintainable code.</p>
<p>The more developers adopt this concept, the better.</p>
<p>As Tracy mentioned in her blog post:</p>
<blockquote>
<p><em>“The more people understand reactive programming, the more productive we as one modern web will all be. The only barrier to adoption is not understanding the paradigm and the language around it.”</em></p>
</blockquote>
<p>I think it’s not such a bad idea to invest some time in learning reactive programming, and RxJS could be a great library to get you started.</p>
<p>Watch the full talk<a href="https://www.youtube.com/watch?v=smBND2pwdUE&amp;feature=youtu.be&amp;t=23m9s">here</a>.</p>
<h2>2. React Navigation</h2>
<p>When implementing navigation inside your React Native app, you have two options: you can use a library that wraps the native navigation APIs for the platform, or you can use a re-implementation of those APIs using the same React Native primitives that you use throughout your app. <a href="https://twitter.com/notbrent">Brent Vatne</a>, who works at Expo, gave a nice talk about an open source project he works on: <a href="https://reactnavigation.org/">React Navigation</a>.</p>
<p>Providing us with a clear overview of how you can use different types like switch or stack navigators throughout your app, Brent gave us a better understanding of how navigation works. He also showed how to do great looking transitions with the React Navigation library.</p>
<p>Besides that, Brent mentioned some great reasons to choose a “JavaScript-based navigation” over a “native-based” one. To learn how to take full advantage of it, I recommend reading the docs of the React Navigation library or check out<a href="https://medium.com/@christian.falch/fluid-transitions-with-react-navigation-a049d2f71494">this post</a>of Christian Falch about Fluid transitions with React Navigation.</p>
<p>Watch the full talk <a href="https://www.youtube.com/watch?v=N-X3Z5A-pW4&amp;feature=youtu.be&amp;t=40m5s">here</a>.</p>
<img src="https://miro.medium.com/freeze/max/38/1*JPhQb0r50aAyc-G35cd8mw.gif?q=20"/>
<img src="https://miro.medium.com/max/1200/1*JPhQb0r50aAyc-G35cd8mw.gif"/>
<p><a href="https://github.com/fram-x/fluidtransitions">Code</a></p>
<h2>3. D3 and React, Together</h2>
<p>One of the most impressive talks of the day was about D3 and React, by<a href="https://twitter.com/sxywu">Shirley Wu</a>. D3 is a library for building data visualisations, and it can work together with React. The challenge of integrating D3 with React is that React and D3 both want to control the DOM. Mainly by letting D3 do all the calculations, and React do all the rendering, Shirley showed us how to deal with this challenge and why D3 and React can work together.</p>
<p>She also demoed <a href="https://pudding.cool/2017/03/hamilton/">this project</a> that is worth checking out. Ending the talk with some live coding in front of a huge crowd showed why Shirley Wu is the real deal, and it made this one of the more interesting talks of the day.</p>
<p>Read more about this subject on <a href="https://medium.com/@sxywu/on-d3-react-and-a-little-bit-of-flux-88a226f328f3">her blog post</a>or watch the full talk<a href="https://www.youtube.com/watch?v=smBND2pwdUE&amp;feature=youtu.be&amp;t=2h36m">here</a>.</p>
<h2>4. React Native VR + AR Made Simple</h2>
<p>The React ecosystem has given developers the opportunity to target platforms that were once thought to be out of reach for JavaScript developers. <a href="https://twitter.com/dabit3">Nader Dabit</a> gave a nice talk about the <a href="https://viromedia.com/">Viro</a> platform that opens the door to developing both Augmented Reality and Virtual Reality.</p>
<p>Creating your own AR app with React Native is actually pretty simple and straightforward if you’re using <a href="https://github.com/viromedia/viro">Viro React</a>. It allows you to create fun apps like Nader’s demo app, which allowed users to upload pictures taken at the conference into a virtual room, as well as walk around and interact with them in AR.</p>
<p>Although this technology can be fun to experiment with, I can’t imagine myself using this technology for any useful implementations. I also think that most professional AR and VR developers are probably not building their stuff with React Native. But if you have the time and a good amount of creativity, you might want to checkout Viro.</p>
<p>Watch the full talk <a href="https://www.youtube.com/watch?v=N-X3Z5A-pW4&amp;feature=youtu.be&amp;t=2h08m30s">here</a>.</p>
<img src="https://miro.medium.com/freeze/max/60/1*4rpcfMytFkK4M4kwy0eN-A.gif?q=20"/>
<img src="https://github.com/viromedia/viro"/>
<h2>5. React State Management in a GraphQL Era</h2>
<p>Last but not the least, I want to mention the great presentation of<a href="https://twitter.com/thekitze">Kristijan Ristovski</a>also known as Kitze. He talked about the (un)necessity of state management libraries when GraphQL takes care of managing data in our apps.</p>
<p>GraphQL is a well-documented data query language that provides an alternative to REST and ad-hoc web service architectures. It allows you to return complex data results through a single API call.</p>
<p>Or as Kitze said it:</p>
<blockquote>
<p>“GraphQL is the thing that’s eventually gonna replicate REST, but you keep telling yourself that you don’t need to learn it.”</p>
</blockquote>
<p>Kitze opened his enjoyable talk with a fun explanation of why the terms “Rockstar-”, “Senior-” or “Ninja-” developer are so stupid, which showed that he had more to talk about then just how great GraphQL is.</p>
<p>He explored all the possibilities and compared the combinations of React, Apollo, Redux, MobX, and Next.js. But instead of just praising all these genius projects, he kept it real and talked about the practical use cases.</p>
<p>For instance, Redux, a brilliant concept that is (over)used by many react developers. Even the creator of Redux said:</p>
<blockquote>
<p>“It’s definitely overhyped, low level and often used unnecessarily”.</p>
</blockquote>
<p>You can read more about that from the creator of Redux in his own blog post,<a href="https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367">You Might Not Need Redux</a>.</p>
<p>Kitze continued and showed a simple question someone asked on Reddit:*“Hello Reddit, I just started my first React app. What should I use to do network requests?”<em>The most upvoted answer?</em>“Just use redux-saga.”*Who says JUST use redux-saga? As Kitz said, that is like having a friend tell you there is a bug in his house and you tell him to use a bazooka.</p>
<p><em>The front-end world is changing very rapidly, and the battles of different technologies will keep on forever. But the problem is that we are asking “What’s better?” instead of:</em></p>
<ul>
<li>What’s suitable for my app?</li>
<li>What’s suitable for my team?</li>
<li>What’s suitable for our use-case?</li>
</ul>
<p>Kitze ended his talk with the question “Do we even need a state management library when using GraphQL?” The answer: maybe.</p>
<p>It all depends on your project. If you want a bit more specific advice, I recommend watching the full talk for some examples.</p>
<img src="https://miro.medium.com/max/60/1*5xM37Bwus9boD9QvLtp7nw.png?q=20"/>
<img src="https://miro.medium.com/max/4000/1*5xM37Bwus9boD9QvLtp7nw.png"/>
<p>Moving from Redux to React Apollo <a href="https://dev-blog.apollodata.com/reducing-our-redux-code-with-react-apollo-5091b9de9c2a">blog</a></p>
<p>The only things left to mention about Kitze’s talk are these simple pieces of advice:</p>
<ul>
<li>Stop seeking external approval.</li>
<li>Stop seeking answers to other people’s projects.</li>
<li><em>Stop feeling insecure about your code, because today no one actually knows what they’re doing</em>.</li>
<li>For God’s sake: delete your Twitter account.</li>
</ul>
<p>Watch the full talk <a href="https://www.youtube.com/watch?v=smBND2pwdUE&amp;feature=youtu.be&amp;t=5h2m44s">here</a>.</p>
<h2>Final thoughts</h2>
<p>Although there were a lot more interesting presentations besides the five talks I mentioned, not all of them were as engaging and interesting. Not every talk was as in depth as we’d hoped, and showing a lot of documentation that can easily be found online will not do it for everyone.</p>
<p>Besides that, most speakers demonstrated how passionate they are and the overall experience was great!</p>
<p>React Amsterdam is a well-organized conference that provided much to learn. Hopefully this post gave you a glimpse of a couple of interesting topics that you may want to dive into deeper.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[How to Create and Publish a Chrome Extension in 20 minutes]]></title>
            <link>https://jakeprins.com/blog/how-to-create-and-publish-a-chrome-extension-in-20-minutes</link>
            <guid>https://jakeprins.com/blog/how-to-create-and-publish-a-chrome-extension-in-20-minutes</guid>
            <pubDate>Sat, 24 Feb 2018 00:00:00 GMT</pubDate>
            <description><![CDATA[Ever wondered what it would be like to create a Chrome extension? I’m here to tell you it’s easy! Follow these steps and to turn your idea into reality. You’ll be publishing a real extension in the Chrome Web Store in no time.]]></description>
            <content:encoded><![CDATA[<p>Ever wondered what it would be like to create a Chrome extension? I’m here to tell you it’s easy!</p>
<p>Follow these steps and to turn your idea into reality. You’ll be publishing a real extension in the <a href="https://chrome.google.com/webstore/category/extensions">Chrome Web Store</a> in no time.</p>
<hr/>
<h2>What is a Chrome Extension?</h2>
<p>Chrome extensions let you add functionality to the Chrome web browser without diving deeply into its native code. That’s awesome because you can create new extensions for Chrome with core technologies that web developers are very familiar with — HTML, CSS, and JavaScript. If you’ve ever built a web page, you’ll be able to create an extension faster than you can have lunch. The only thing you need to learn is how to add some functionality to Chrome through some of the JavaScript APIs that Chrome exposes.</p>
<p>If you’re not yet experienced building web pages, I recommend you first dive into some free resources to learn how to code, like <a href="https://www.freecodecamp.org/">freeCodeCamp</a>.</p>
<hr/>
<h2>What Do You Want to Build?</h2>
<p>Before you start, you should have a rough idea of what you want to build. It doesn’t need to be a groundbreaking new idea, you can just do it for fun. In this article, I’m going to tell you about my idea and how I implemented itm making a Chrome extension.</p>
<hr/>
<h2>The Plan</h2>
<p>I’ve been using the <a href="https://chrome.google.com/webstore/detail/unsplash-instant/pejkokffkapolfffcgbmdmhdelanoaih">Unsplash</a> Chrome extension for a while. It allows me to have nice background images from <a href="https://unsplash.com/">Unsplash</a> in my default tab. I later replaced it with the <a href="https://muz.li/">Muzli</a> Chrome extension that turns the default tab into a feed of design news and shots from around the web.</p>
<p>Let’s use these two extensions as inspiration to build something new — this time for movie lovers. My idea is to show a random background image of a movie every time you open a new tab. On scrolling, it turns into a nice feed of popular movies and TV shows. Let’s get started.</p>
<hr/>
<h2>Step 1: Setting Things Up</h2>
<p>The first step is to create a manifest file, <code>manifest.json</code>. This is a metadata file in JSON format that contains properties like your extension’s name, description, version number, and so on. In this file, we tell Chrome what the extension is going to do, and what permissions it requires.</p>
<p>For the movie extension, we need permission to control <code>activeTab</code>, so our <code>manifest.json</code> file looks something like this:</p>
<pre><code>{
 &quot;manifest\_version&quot;: 2,
 &quot;name&quot;: &quot;Cool name&quot;,
 &quot;description&quot;: &quot;Super cool extension&quot;,
 &quot;version&quot;: &quot;1&quot;,
 &quot;author&quot;: &quot;Your name&quot;, &quot;browser\_action&quot;: {
   &quot;default\_icon&quot;: &quot;tab-icon.png&quot;,
   &quot;default\_title&quot;: &quot;Have a good day&quot;
 }, &quot;chrome\_url\_overrides&quot; : {
  &quot;newtab&quot;: &quot;newtab.html&quot;
 }, &quot;permissions&quot;: \[&quot;activeTab&quot;\]
}
</code></pre>
<p>As you can see, <code>newtab.html</code> is the HTML file that should be rendered every time a new tab gets opened. To do this we need permission to control the <code>activeTab</code>, so when a user tries to install the extension they’re warned about all the permissions the extension needs.</p>
<img alt="Image for post" class="s t u da ai" src="https://miro.medium.com/max/467/1*jMmZo8AUvcf01GMxTnXqZg.png" width="467" height="140"/>
<p>Another interesting thing inside the <code>manifest.json</code> are the browser actions. In this example, we use it to set the title, but there are other options as well. For instance, to show a popup whenever you click on the app icon inside the address bar, all you have to do is something like this:</p>
<pre><code>&quot;browser\_action&quot;: {
  &quot;default\_popup&quot;: &quot;popup.html&quot;,
 },
</code></pre>
<p>Now, <code>popup.html</code> will be rendered inside the popup window that&#x27;s created in response to a user&#x27;s click on the browser action. It&#x27;s a standard HTML file, so it’s giving you free rein over what the popup displays. Just put some of your magic inside a file named <code>popup.html</code>.</p>
<hr/>
<h2>Step 2: Test Whether It Works</h2>
<p>The next step is to create the <code>newtab.html</code> file and put in a &quot;<code>Hello world</code>”:</p>
<pre><code>&lt;!doctype html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;Test&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello World!&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>To test it, visit <code>chrome://extensions</code> in your browser and ensure that the “Developer mode” checkbox in the top right-hand corner is checked.</p>
<img alt="Image for post" class="s t u da ai" src="https://miro.medium.com/max/700/1*6eljnFW72RcJMOKxF2c-ag.png" width="1524" height="342"/>
<p>Chrome Developer mode</p>
<p>Click “Load unpacked extension” and select the directory in which your extension files live. If the extension is valid, it will be active straight away, so you can open a new tab to see your “Hello world.”</p>
<hr/>
<h2>Step 3: Making Things Nice</h2>
<p>Now we have our first feature working, it’s time to make it look nice. We can style our new tab simply by creating a <code>main.css</code> file in our extension directory and loading it in our <code>newtab.html</code> file. The same goes when including a JavaScript file for any active functionality that you want to include. Assuming that you’ve created a web page before, you can now use your magic to show your users whatever you want.</p>
<h2>Finishing the plan</h2>
<p>All I further needed to finish the movie extension was HTML, CSS, and JavaScript, so I don’t think it’s relevant to dive deep into the code, but I’d like to go through it quickly.</p>
<p>Here’s what I did. For my idea, I needed some nice background images, so, in the JavaScript file, I used the <a href="https://www.themoviedb.org/">TMDb API</a> to fetch some popular movies, took their backdrop images, and put them in an array. Now, whenever the page loads it randomly picks one image from that array and sets it as the background of the page. To make this page a little more interesting I also added the current date in the top right corner. And for more information, it allows users to click the background, which links to the movie’s <a href="http://www.imdb.com/">IMDb</a> page.</p>
<p>I replaced the screen with a nice feed of popular movies when the user tries to scroll down. I used the same API to build cards of movies with an image, title, rating, and vote count. On clicking one of those cards, it shows the overview with a button to watch a trailer.</p>
<h2>The result</h2>
<p>Now, with that little <code>manifest.json</code> file and just some HTML, CSS and JavaScript, every new tab that you open looks a lot more interesting:</p>
<img alt="Image for post" class="s t u da ai" src="https://miro.medium.com/max/640/1*P8NTRn4MiIARJCM83SUxKg.gif" width="640" height="311"/>
<p>View the end result <a href="https://chrome.google.com/webstore/detail/raterfox-popular-movies-t/pbmdibcifmempicdafabdakcoamfobik">here</a>.</p>
<hr/>
<h2>Step 4: Publish Your Extension</h2>
<p>When your first Chrome extension looks nice and works as it should, it’s time to publish it to the Chrome Store.</p>
<p>Simply follow <a href="https://chrome.google.com/webstore/developer/dashboard">this link</a> to go to your Chrome Web Store dashboard (you’ll be asked to sign in to your Google account if you aren’t already). Then click the “Add new item” button, accept the terms, and you’ll go to the page where you can upload your extension. Compress the folder that contains your project and upload that ZIP file.</p>
<img alt="Image for post" class="s t u da ai" src="https://miro.medium.com/max/700/1*qZs4NLeppLeQl_StCG7mGw.png" width="1984" height="500"/>
<p>Chrome Web Store</p>
<p>After successfully uploading your file, you’ll see a form where you should add some information about your extension. You can add an icon, a detailed description, upload some screenshots, and so on.</p>
<p>Make sure you provide some nice images to show off your project. The store can use these images to promote your groundbreaking project. The more images you provide, the more prominently your extension will be featured. You can preview how your extension looks inside the web store by clicking the “Preview changes” button. When you’re happy with the result, hit “Publish changes” and that’s it, you’re done!</p>
<p>Go to the <a href="https://chrome.google.com/webstore/category/extensions">Chrome Web Store</a> and search for your extension by its title (it might take some time before it’s up there). If you’re interested, you can find mine <a href="https://chrome.google.com/webstore/detail/raterfox-popular-movies-t/pbmdibcifmempicdafabdakcoamfobik">here</a>.</p>
<p>The only thing left to do is get some users. So, share a post about your life-changing Chrome extension on social media, tell your friends to check it out, add it to <a href="https://www.producthunt.com/posts/raterfox">ProductHunt</a>, and don’t forget to share your project here in the comments — I can’t wait to see what you came up with!</p>
<hr/>
<h2>Conclusion</h2>
<p>As a web developer, it’s easy to quickly create a Chrome extension. All you need is some HTML, CSS, JavaScript, and basic knowledge of how to add functionality through some of the JavaScript APIs that Chrome exposes. Your initial setup can be published inside the Chrome Web Store within 20 minutes. Building an extension that’s new, worthwhile, or looks nice will take a bit more time. But it’s all up to you!</p>
<p>Use your creativity to come up with something interesting and if you ever get stuck, the excellent Chrome extension <a href="https://developer.chrome.com/extensions">documentation</a> can probably help you out.</p>
<hr/>
<p>What are you waiting for? Time to start working on your own Chrome extension and turning your idea into reality.</p>
<p>Don’t forget to share your project in the comments and any questions or feedback.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[Why side projects are so damn important]]></title>
            <link>https://jakeprins.com/blog/why-side-projects-are-so-damn-important</link>
            <guid>https://jakeprins.com/blog/why-side-projects-are-so-damn-important</guid>
            <pubDate>Tue, 19 Dec 2017 00:00:00 GMT</pubDate>
            <description><![CDATA[Side projects come in many forms and have lots of different purposes. Some people start them to create a nice product and eventually build a successful company, but there are many other reasons why working on side projects can be exceptionally important.]]></description>
            <content:encoded><![CDATA[<h1>Why side projects are so damn important</h1>
<p>Trello, Craigslist, Unsplash, AppSumo, Twitter, Gmail… what do all these successful companies have in common?</p>
<p>Yes, they can all trace their humble beginnings back to being something we might call a ‘side project’.</p>
<p>Side projects come in many forms and have lots of different purposes. Some people start them to create a nice product and eventually build a successful company, but there are many other reasons why working on side projects can be exceptionally important.</p>
<h3>Accelerate your learning.</h3>
<p>Learning and mastering a new skill can be tough, but with enough persistence and motivation, there is a lot we can achieve. Like learning how to code, it can be hard, frustrating and time-consuming. In the end, the most important thing to do is to keep going and gain experience.</p>
<p>Sure, it helps if you’re good at math, logical thinking and have a college degree in computer science. But for people with a non-technical background, like me, it’s still possible to become great developers with enough motivation and persistence.</p>
<p>In my own experience, I found working on side projects in my spare time not only fun but really helpful in developing my coding skills.</p>
<p>Every time I read something interesting on a blog, or was facing a challenge at work, or learned something new from a colleague, I wanted to implement it in one of my side projects as well.</p>
<p>Read about a better way to store images? Let’s try it! Learned how to work with AJAX? Let’s Ajax-ify everything! Stumbled upon a great Ruby gem? Let’s bundle that up!</p>
<p>And every time I improved my skills, I wanted to rewrite stuff to use better or cleaner code, which made me care about it more and more.</p>
<h3><strong>RaterFox</strong></h3>
<p><a href="https://42hire.com/my-first-months-as-a-web-developer-13127591432d">In my first year working as a developer</a> I learned a lot. I also believe the side projects I worked on at night or over weekends gave my learning ability and motivation a big boost.</p>
<p>One of the projects I spend by far the most time on is called <a href="https://www.raterfox.com/">RaterFox</a>. It’s a social platform for movies and TV shows that amongst other things, allows you to rate and review titles or recommend them to your friends.</p>
<p>I’ve used the <a href="https://www.themoviedb.org/">TMDb</a> API to get the movie and TV data. I used the <a href="https://giphy.com/">Giphy</a> API to create a gif picker so users can create ‘Reactions’ to show their initial thoughts about a movie or TV series.</p>
<p>I’m still working on improvements (because when will a project ever be finished?), but in case you’re interested you can check it out <a href="http://www.raterfox.com/">here</a>. BTW, any feedback is always welcome and highly appreciated!</p>
<p><img src="https://miro.medium.com/max/1280/1*wtg1ehIh4YzWtFEDx4V_fw.gif" alt=""/></p>
<h3>When side projects shine</h3>
<p>Working on side projects can have different purposes. These can be improving some skills, experimenting with new technologies, testing out a product idea or building a startup.</p>
<p>But no matter what the goal may be, it’s the perfect way to create something new. Working on a side project is different from “regular” work because there is less pressure, no deadlines, and no rules. You’re free to play around and switch to something else whenever you feel like it.</p>
<p>This freedom sometimes may lead to unproductive, glitchy and half working “products”, but it can also result in something creative, inspiring and innovative. And that is when the beauty of a side project will shine.</p>
<p>At <a href="https://www.inspire.nl/">the company</a> I work for, we have an ‘Experiment Day’ every month. This is great for trying out some interesting technologies. Similar ideas are found across a lot of tech companies — and it’s easy to see why.</p>
<p>For example, Facebook’s “Like” button started as the “awesome button” and was created during a Facebook hack-a-thon. This simple functionality is now a core feature and is used in various guises in all types of digital products.</p>
<p>During the hack-a-thon, the creators didn’t know that they were building a button that would have such an impact, but it did.</p>
<p>The huge tech giants like Facebook and Google inspired smaller companies to spend more time on side projects because the results were seen everywhere.</p>
<p>Gmail, Google talk, AdSense and Google News are some of the successful projects that were born out of Google’s famous “20% policy”. This allows employees to use up to 20 percent of their workweek at Google to pursue their own projects.</p>
<h3>More success stories</h3>
<p>Besides the awesome products that many of us use every day, like Gmail or Instagram, there are many more success stories of startups that are build out of side projects.</p>
<p>Take for instance <a href="https://unsplash.com/">Unsplash,</a> the side project that did more than the creators could ever imagine: it saved their startup. Unsplash, which provides copyright-free and high-quality images, started off as a marketplace for hiring freelance designers and developers. It failed to gain traction until their side project (that was initially built in one afternoon) started to lift off.</p>
<p>The Crew team, the company beside Unsplash, launched many other side projects that have become the main sources of referral traffic to the Crew website.</p>
<blockquote>
<p>“More people cared about us in a few hours than in the entire last year.”</p>
</blockquote>
<p>If you are a developer, you likely have an account on Github, the Web-based Git version control repository hosting service (oh, and billion-dollar company).</p>
<p>Founders Chris Wanstrath and PJ Hyett were upset with how difficult it was to change open source code, so they built their own repository. They built their side project at nights and weekends and now serve over 20 million users.</p>
<blockquote>
<p>“It all started with a domain, a cheap slice from Slicehost, and some stock art.”</p>
</blockquote>
<p>You never know what you may end up with when you start a side project.</p>
<p>Take for instance The Point, a social network that connected users who wanted to rally behind a specific cause.</p>
<p>When founder Eric Lefkofsky saw users banding together to buy an item in bulk and receive a discount, he made some new plans for his project and ended up with <a href="http://www.groupon.com/">Groupon</a>… you know, that daily deals site that had a $1 billion valuation within two years of launching.</p>
<p>Another daily deal site you may have heard of is <a href="http://www.appsumo.com/">AppSumo</a>. This startup shows that you don’t need a lot of money to start. Founder Noah Kagan had the idea of a discount site for online companies but only started with $50 to build a landing page and to collect emails. By investing time and effort he ended up with a startup that had $1 million in sales in their first year.</p>
<p>Now, it’s awesome to read about these side projects that lifted off to become great companies, but as I mentioned before, working on side projects could lead to many different benefits.</p>
<p>There are plenty of examples of people who learned how to code just by working on side projects. So if you want to learn a new programming language, improve your current skills, or use your creativity to solve a problem you faced, it’s never too late to start something new. So, how to start?</p>
<h3>How to start</h3>
<p>Once a while, we all have great idea’s running through our mind, but most of the time we don’t act upon them.</p>
<p>It’s not a mystery why. Ideas are not always realistic, but it’s not necessary to let you be overwhelmed by the size of some of our ideas and the thoughts of actually building it. Three things to keep in mind if you want to start on a new project:</p>
<h3>1. Start small</h3>
<p>The easiest way to start is to pick a small project you will find interesting, or will solve a problem you have. Then break it down into smaller components until one of them looks easy to implement. Complete that part and repeat the process until you have all of the parts you need to solve the original problem.</p>
<h3>2. Keep it simple</h3>
<p>The best approach is to keep it simple. Your project should have minimal complexity, minimal coding, and be focused on the main purpose it serves. If it’s an app that lets you find new recipes, it doesn’t need chat functionalities. You can always add new things later, but when you are just starting you should reduce features to the minimum.</p>
<h3>3. No pressure</h3>
<p>Starting a new project can be exciting. Although it’s fun to read about the success stories about projects that were converted into successful companies, one of the reasons why working on side projects can be so interesting is that it shouldn’t give you pressure to make money with it.</p>
<p>That means it also doesn’t matter if it fails. No deadlines and complete creative freedom mean you can choose which way you think your project should go. While gaining experience in your work, and doing something you love, you can build something to care about which will make you keep coming back when there’s more work to do.</p>
<p>Don’t dismiss your side project as just this thing you do on the side. It can become the thing you do that makes you happy and excited. Your little side project maybe even evolves into your “main project” one day. Just start small, be patient and improve it while you learn. You may end up with some unexpected products that the world didn’t know it needed.</p>
<p>Thanks for reading. Got a nice side project you work on? Share it in the comments!</p>
<p>// @NOTE: You can find me on Twitter @jake_prins .</p>
<p>// @NOTE: This was originally posted on Medium like 2 years ago.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
        <item>
            <title><![CDATA[My first months as a full-time web developer]]></title>
            <link>https://jakeprins.com/blog/my-first-months-as-a-web-developer</link>
            <guid>https://jakeprins.com/blog/my-first-months-as-a-web-developer</guid>
            <pubDate>Fri, 24 Jun 2016 00:00:00 GMT</pubDate>
            <description><![CDATA[In the beginning of 2016, I started on my first full-time job as a professional web developer. A couple of months before this I had finished an intense developer’s boot camp program. This is a summary of my experiences.]]></description>
            <content:encoded><![CDATA[<h1><strong>My first months as a web developer</strong></h1>
<p>At the start of 2016, I started on my first job as a web developer at <a href="http://www.inspire.nl">Inspire</a>. A couple of months before this I had finished an intense developer’s boot camp program. If you want to know what that was like you can read the blog of a fellow student <a href="http://webhippy.com/tag/codaisseur/">here</a>.</p>
<p>Not very long before I started that program I didn’t even know what a <em>class</em> or an <em>object</em> was in programming, and now I’m working on big and complex web applications that are used by thousands of people. If you are interested in what it’s like to switch a career to become a developer, you can read my first Medium post: ‘<a href="https://medium.com/@jakeprins23/when-athletes-become-programmers-a815c5357af1"><em>When athletes become developers</em></a><em>’</em>, but in this post, I partially zoom in to my personal experience as a starting web developer and even give some advice for new developers who are looking for a nice company to write code for.</p>
<p>I have to add that this may not only be interesting for new developers, but also for employers who are curious about what they can do to successfully add juniors to their team.</p>
<h2><strong>The company</strong></h2>
<p>The experience of working as a developer for the first time is of course impacted a lot by the type of company you work for. To give a quick summary, the company builds software for clients with different backgrounds, but most companies or organizations that have social relevance and are focused on improving people’s lives such as health care, educational publishers, research institutes, charities, and startups. At the moment it has about 10 employees and is based in the center of Utrecht, the Netherlands. Most applications they build are in Ruby on Rails and the developers are mostly very experienced.</p>
<h2><strong>What’s it like?</strong></h2>
<p>In my first days of working at Inspire, I and another intern got the assignment to improve the Team Happiness application that was built and used by the company itself. It’s a small app that basically sends an email every week, at a random day and time, to employees asking what their team&#x27;s happiness is at that moment. Employees can reply with one of the five smileys, giving a score to how happy they are at that moment.</p>
<p>This application says a lot about the vision of the company and shows that it really cares about the employers. This is expressed in multiple ways, like the monthly one-on-one conversations with the founder, the flexibility to occasionally work from home, or the experimentation-days.</p>
<blockquote>
<p>Experimentation-day is every last Thursday of the month and that day can be spend completely on learning a new technology that we’re interested in.</p>
</blockquote>
<p>The nice thing about working on the Happiness App for the first days was that the consequences were not that great if you made any mistakes, because it’s only used by ourselves. This took off a lot of the pressure when your first pull request is going to be merged. Also, because every line of code is always reviewed by another developer, it’s very nice to get feedback on how to write better and cleaner code. This is one of the nice things about working in an environment of experienced developers.</p>
<p>After the first couple of weeks, I joined the team that was working on a much bigger and more complex application. By mostly working on small bugs and styling issues, I slowly started to get a nice feel about the whole project. Most of the time I could always ask someone for help so I didn’t have to stay stuck. But even with all the good intentions, it can still be a bit disturbing for colleagues when they are intensely focused on solving some big issues, to have somebody asking them about “easy” stuff all the time. So if you are hoping to start working on your first developer job soon, be prepared to figure a lot of things out yourself. Of course, the best friend of every developer, Google, is always there to have your back.</p>
<blockquote>
<p>while ( ! ( succeed = try ( ) ) );</p>
</blockquote>
<p>You will run into problems that look like you just can’t solve them at the moment, but most likely someone else did as well, so it’s really nice that there is an amazing community actively helping each other on platforms like <a href="http://stackoverflow.com/">StackOverflow</a>. I’m not familiar with every programming language, but the Ruby community is great and can be your saving buddy in moments when you’re totally frustrated about things not working as you want them to.</p>
<h2><strong>Things to think about for starting developers</strong></h2>
<p><em>Which players are on my team?</em></p>
<p>Most of the time learning to code is a process that you do by yourself. You try, fail, try again, fail again, try another time, and fail another time. While thinking, trying, and reading, you break this infinite loop of failure with small achievements of success, only to be followed by that same loop again. It sounds depressing, but it’s not. It takes effort and determination to continue trying, but that process is what’s actually making you slowly become a better and smarter developer. That said, learning to write good code will take a lot more of your time and effort if you don’t have access to help, tips, tricks and code explanations from experienced people. When I first started to learn how to code, my best feedback has always been the fact that what I’m trying to achieve is working or that it’s giving me errors. For me, there was no difference between good and bad code, only code that’s working and code that’s not. Of course, my first pull request at Inspire had a lot of comments, and I was amazed by how much more reliable and cleaner my code looked after I finally had improved it together with a colleague so that my PR was mergeable. Having colleagues around you that can help you not only provide the understanding of every line of code but also the reason why certain code can be best written in a certain way, is an extreme advantage.</p>
<p><em>Do they have the time?</em></p>
<p>If you are looking for your first job as a developer, it’s very important to know if the company also has the capability of giving you the time you need to spend on educational needs. If there is only one medior/senior developer working at your next “dream job”, don’t expect him to be available to you every time when you might need some help. He is probably working on a lot of things that are usually more urgent than your personal development. One of the nice things about Inspire is that they have given me the responsibility to make sure I spend enough time learning. This means that I should schedule pair-programming sessions with colleagues and use some of my work time particularly for educational purposes like following a JavaScript course on Code School. Combining actual work with theoretical learning is a great way to develop your skills in your early development career.</p>
<p><em>Why do they want me?</em></p>
<p>Lots of startups are looking for junior developers and sometimes for the wrong reasons. If they are looking for a young developer because it saves them money, you can get in trouble if they have big expectations and small technical knowledge. It can be awesome to work at a nice new startup, but it can be a good thing if you first figure out if they have realistic expectations of you. You’re probably not yet ready to build the next Airbnb of whatever just by yourself.</p>
<p>To hire someone for an internship or a junior position can be a big investment for a company. You have to learn a lot on the job, your time may not yet be billable to clients and you need more help from colleagues than mediors/seniors do, but learning on the job and spending at least 40 hours a week coding will boost up your skills really fast. If you are a self-taught developer or just really passionate about programming, you shouldn’t be doubtful about going out for job interviews. I know it seems that most companies are just looking for experienced developers, but to build a great development team it’s also important to add juniors (read <a href="https://medium.com/javascript-scene/the-essential-guide-to-building-balanced-development-teams-b051a62acc80">here</a> why). There are a lot of companies looking for developers and sometimes you just have to be a bit lucky to find one that really fits you. If you are looking for your first job as a web developer you can find some great tips over <a href="https://medium.com/javascript-scene/how-to-land-your-first-development-job-in-5-simple-steps-4e9fb73314c">here</a>, or you could read <a href="https://medium.freecodecamp.com/how-i-landed-my-first-job-as-a-web-developer-f991fe728b95#.zij9rqq9v">this</a> nice blog post.</p>]]></content:encoded>
            <author>spencer@planetaria.tech (Jake Prins)</author>
        </item>
    </channel>
</rss>