<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zul Ikram Musaddik Rayat</title>
    <description>The latest articles on DEV Community by Zul Ikram Musaddik Rayat (@devrayat000).</description>
    <link>https://dev.to/devrayat000</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F867084%2F50a0e027-0439-4c8a-8733-1cad7dff769b.jpg</url>
      <title>DEV Community: Zul Ikram Musaddik Rayat</title>
      <link>https://dev.to/devrayat000</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devrayat000"/>
    <language>en</language>
    <item>
      <title>Email Verification with Better-Auth (Basics Tutorial, Ep. 2)</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Sun, 21 Sep 2025 16:27:58 +0000</pubDate>
      <link>https://dev.to/devrayat000/email-verification-with-better-auth-basics-tutorial-ep-2-3mm3</link>
      <guid>https://dev.to/devrayat000/email-verification-with-better-auth-basics-tutorial-ep-2-3mm3</guid>
      <description>&lt;p&gt;Welcome back to the &lt;a href="https://www.youtube.com/playlist?list=PLKNCWppgn6elkj28NRedKURDMAp-cuIJm" rel="noopener noreferrer"&gt;&lt;strong&gt;Better-Auth Basics&lt;/strong&gt;&lt;/a&gt; series! 🚀  &lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/devrayat000/authentication-using-better-auth-basics-tutorial-1bc1"&gt;Episode 1&lt;/a&gt;, we set up &lt;strong&gt;Better-Auth&lt;/strong&gt; with Drizzle ORM and implemented sign-up and login functionality. It worked great… but there’s a big flaw:  &lt;/p&gt;

&lt;p&gt;👉 Anyone can sign up with &lt;strong&gt;any random email&lt;/strong&gt; — even one they don’t own.  &lt;/p&gt;

&lt;p&gt;That’s obviously not safe for production. So in this post, we’ll fix that by adding &lt;strong&gt;Email Verification&lt;/strong&gt; with Better-Auth.  &lt;/p&gt;




&lt;h2&gt;
  
  
  ❌ The Problem Without Verification
&lt;/h2&gt;

&lt;p&gt;Right now, a user can type &lt;strong&gt;any email address&lt;/strong&gt; on the registration page, and the server will happily accept it.  &lt;/p&gt;

&lt;p&gt;That means fake accounts, spam signups, and security risks. We need a way to make sure the person &lt;strong&gt;actually owns the email&lt;/strong&gt; they’re registering with.  &lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Email Verification with Better-Auth
&lt;/h2&gt;

&lt;p&gt;The good news? Better-Auth already has &lt;strong&gt;email verification built in&lt;/strong&gt; for email/password authentication. We just need to configure it. Let’s go step by step.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 Step 1: Configure an Email Provider
&lt;/h2&gt;

&lt;p&gt;For sending verification emails, I used &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Resend account
&lt;/li&gt;
&lt;li&gt;Add your custom domain
&lt;/li&gt;
&lt;li&gt;Generate an API key
&lt;/li&gt;
&lt;li&gt;Save it in your &lt;code&gt;.env&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-api-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠 Step 2: Create Email Templates
&lt;/h2&gt;

&lt;p&gt;Inside Resend, configure simple templates for your verification emails.&lt;br&gt;
This is the message your users will see in their inbox with the verification link.&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠 Step 3: Enable Email Verification in Better-Auth
&lt;/h2&gt;

&lt;p&gt;In your Better-Auth configuration, enable the verification features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;betterAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;better-auth/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../db/schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betterAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;emailAndPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requireEmailVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;emailVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;sendOnSignUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;autoSignInAfterVerification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;sendVerificationEmail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendVerificationEmail&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠 Step 4: Send Verification Email on Sign Up
&lt;/h2&gt;

&lt;p&gt;Better-Auth will now automatically send a verification email when a new user registers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After sign-up, redirect the user to a verify page in your app.&lt;/li&gt;
&lt;li&gt;Tell them to check their inbox for the verification link.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/verify/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VerifyPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Verify Your Email&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        We’ve sent you a link. Please check your inbox and click it to activate your account.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠 Step 5: Testing the Flow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Register with a real email address&lt;/li&gt;
&lt;li&gt;Check your inbox → you’ll see the verification email&lt;/li&gt;
&lt;li&gt;Click the link → the server validates it&lt;/li&gt;
&lt;li&gt;You’re automatically signed in and redirected to the homepage 🎉&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what the successful response looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email verified and user signed in"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎉 And That’s It!
&lt;/h2&gt;

&lt;p&gt;With just a few lines of configuration, we added secure email verification to our Better-Auth setup.&lt;/p&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users must prove they own their email&lt;/li&gt;
&lt;li&gt;Fake signups are blocked&lt;/li&gt;
&lt;li&gt;Onboarding is safer and more production-ready&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 What’s Next?
&lt;/h2&gt;

&lt;p&gt;This was Episode 2 of Better-Auth Basics.&lt;br&gt;
In future episodes, we’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👥 Role-based authentication&lt;/li&gt;
&lt;li&gt;⚡ Rate limiting&lt;/li&gt;
&lt;li&gt;🛡 Middleware for protecting routes
Stay tuned — we’re just getting started.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Stay Connected
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📽️ Youtube: &lt;a href="https://www.youtube.com/@code_unhinged" rel="noopener noreferrer"&gt;code_unhinged&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐦 Twitter (X): &lt;a href="https://x.com/zul_rayat" rel="noopener noreferrer"&gt;zul_rayat&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 GitHub: &lt;a href="https://x.com/zul_rayat" rel="noopener noreferrer"&gt;devrayat000&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💼 LinkedIn: &lt;a href="https://www.linkedin.com/in/zim-rayat" rel="noopener noreferrer"&gt;zim-rayat&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📷 Instagram: &lt;a href="https://www.instagram.com/rayatttttttt/" rel="noopener noreferrer"&gt;rayatttttttt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 Facebook: &lt;a href="https://www.facebook.com/rayat.ass" rel="noopener noreferrer"&gt;rayat.ass&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 Got questions? Drop them in the comments — I reply to every one!&lt;br&gt;
👍 Don’t forget to like, share, and subscribe for more dev content.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Authentication Using Better-Auth (Basics Tutorial)</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Wed, 17 Sep 2025 00:04:40 +0000</pubDate>
      <link>https://dev.to/devrayat000/authentication-using-better-auth-basics-tutorial-1bc1</link>
      <guid>https://dev.to/devrayat000/authentication-using-better-auth-basics-tutorial-1bc1</guid>
      <description>&lt;p&gt;What’s up, devs! 👋&lt;br&gt;
In this post, I’ll walk you through &lt;strong&gt;setting up authentication in a Node.js/Next.js project using Better-Auth&lt;/strong&gt; — a powerful new authentication library. This is based on the first episode of my YouTube playlist &lt;strong&gt;&lt;a href="https://www.youtube.com/playlist?list=PLKNCWppgn6elkj28NRedKURDMAp-cuIJm" rel="noopener noreferrer"&gt;Better-Auth Basics&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Better-Auth makes it really easy to implement secure authentication with features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Email + Password authentication&lt;/li&gt;
&lt;li&gt;✅ Social sign-ins&lt;/li&gt;
&lt;li&gt;✅ Built-in rate limiting&lt;/li&gt;
&lt;li&gt;✅ Automatic database management &amp;amp; adapters&lt;/li&gt;
&lt;li&gt;✅ Two-factor authentication support&lt;/li&gt;
&lt;li&gt;✅ Simple client API for frontend integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds exciting? Let’s dive in! 🚀&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠 Project Setup
&lt;/h2&gt;

&lt;p&gt;For this tutorial, I’m working on my personal project — a ride-sharing app.&lt;br&gt;
We’ll integrate Better-Auth into it step by step.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Install Dependencies
&lt;/h3&gt;

&lt;p&gt;We’ll need Better-Auth, Drizzle ORM, and Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun add better-auth drizzle-orm postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Environment Variables
&lt;/h3&gt;

&lt;p&gt;Generate a secret key and add it to your .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;BETTER_AUTH_SECRET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-secret-key&lt;/span&gt;
&lt;span class="py"&gt;BETTER_AUTH_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000 # Base URL of your app&lt;/span&gt;
&lt;span class="py"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgresql://user:password@localhost:5432/dbname&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⚙️ Database Setup with Drizzle
&lt;/h2&gt;

&lt;p&gt;Better-Auth ships with ready-to-use schemas.&lt;br&gt;
We’ll copy those into our project and run migrations.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Generate Schema
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx @better-auth/cli generate &lt;span class="c"&gt;# create better-auth schema&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Run Migrations
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bunx drizzle-kit generate &lt;span class="c"&gt;# generate migration files&lt;/span&gt;
bunx drizzle-kit migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once done, your database will have all the required tables.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔑 Enable Email/Password Authentication
&lt;/h2&gt;

&lt;p&gt;Inside your &lt;strong&gt;Better-Auth server setup&lt;/strong&gt;, enable email/password auth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/auth.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;betterAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;better-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;drizzleAdapter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;better-auth/adapters/drizzle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// your drizzle instance&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/db/schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// your drizzle schema&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;betterAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;drizzleAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or "mysql", "sqlite"&lt;/span&gt;
    &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;emailAndPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🖥 Setting Up the Client
&lt;/h2&gt;

&lt;p&gt;Better-Auth provides a client utility to make frontend integration simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/auth-client.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createAuthClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;better-auth/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAuthClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&gt;/** The base URL of the server (optional if you're using the same domain) */&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📡 Next.js API Routes
&lt;/h2&gt;

&lt;p&gt;Now, let’s connect it with Next.js routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/auth/[...all]/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toNextJsHandler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;better-auth/next-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toNextJsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route will handle all sign-in, sign-up, and session management APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  👤 Implement Sign-Up
&lt;/h2&gt;

&lt;p&gt;On your &lt;strong&gt;register page&lt;/strong&gt;, use the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;authClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callbackUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔐 Implement Sign-In
&lt;/h2&gt;

&lt;p&gt;Similarly, on your login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;authClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;callbackUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎉 Testing It Out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tried signing up → User was created successfully ✅&lt;/li&gt;
&lt;li&gt;Tried logging in → Redirected to homepage with 200 response ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it! We now have a working authentication flow with Better-Auth.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 What’s Next?
&lt;/h2&gt;

&lt;p&gt;This tutorial only covers the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up Better-Auth&lt;/li&gt;
&lt;li&gt;Running database migrations with Drizzle&lt;/li&gt;
&lt;li&gt;Implementing Sign-In &amp;amp; Sign-Up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In upcoming posts/videos, I’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔑 Authorization &amp;amp; Role-Based Access&lt;/li&gt;
&lt;li&gt;⚡ Rate Limiting&lt;/li&gt;
&lt;li&gt;🔒 Two-Factor Authentication&lt;/li&gt;
&lt;li&gt;🔗 Social Logins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Connect With Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🐦 &lt;a href="https://www.facebook.com/rayat.ass" rel="noopener noreferrer"&gt;Facebook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;a href="https://github.com/devrayat000" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💼 &lt;a href="https://www.linkedin.com/in/zim-rayat" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📽️ &lt;a href="https://www.youtube.com/@code_unhinged" rel="noopener noreferrer"&gt;Youtube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 If you found this useful, drop a comment and let me know what you’re building with Better-Auth.&lt;br&gt;
And don’t forget to &lt;strong&gt;follow me here on DEV.to + subscribe on YouTube&lt;/strong&gt; for more tutorials.&lt;/p&gt;

&lt;p&gt;Happy coding! ✨&lt;/p&gt;

</description>
      <category>betterauth</category>
      <category>nextjs</category>
      <category>drizzle</category>
      <category>fullstack</category>
    </item>
    <item>
      <title>🍳 Recipe Generator – What’s in Your Pantry?</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Sun, 14 Sep 2025 20:33:41 +0000</pubDate>
      <link>https://dev.to/devrayat000/recipe-generator-whats-in-your-pantry-4lnk</link>
      <guid>https://dev.to/devrayat000/recipe-generator-whats-in-your-pantry-4lnk</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Have a few random ingredients lying around but no idea what to cook?&lt;br&gt;
&lt;strong&gt;Recipe Generator&lt;/strong&gt; helps you turn everyday pantry items into delicious meals.&lt;/p&gt;

&lt;p&gt;Simply type or select ingredients you already have, and the app instantly generates recipe ideas — complete with images, instructions, and serving tips.&lt;/p&gt;

&lt;p&gt;This makes meal planning fun, reduces food waste, and gives home cooks an easy way to experiment with new dishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://gemini-recipe-generator-134638118864.us-west1.run.app" rel="noopener noreferrer"&gt;Live Demo Link&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv31rpqhwql48uguuhs6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv31rpqhwql48uguuhs6v.png" alt="Intro"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqtp1zuckyy5n4v7wqci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwqtp1zuckyy5n4v7wqci.png" alt="Start Generating"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwey01oiqroyby5m1k7kn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwey01oiqroyby5m1k7kn.png" alt="Recipe 1"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivgp7t51rh1awoirklvt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivgp7t51rh1awoirklvt.png" alt="Recipe 2"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvf3aozsbq1tahp32vgxh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvf3aozsbq1tahp32vgxh.png" alt="Recipe 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Google AI Studio
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built the app on &lt;strong&gt;Google AI Studio&lt;/strong&gt;, deployed via &lt;strong&gt;Cloud Run&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Used Gemini’s &lt;strong&gt;multimodal capabilities&lt;/strong&gt; for:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text understanding&lt;/strong&gt;: Parsing ingredient inputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image generation&lt;/strong&gt;: Producing realistic dish images that match the recipe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recipe generation&lt;/strong&gt;: Turning ingredient lists into step-by-step cooking instructions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ingredient → Recipe (Text)&lt;/strong&gt;: Natural language input is converted into structured recipes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recipe → Image (Visual)&lt;/strong&gt;: Each generated recipe is paired with a dish image for inspiration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instructional Output&lt;/strong&gt;: Clear step-by-step cooking instructions with serving notes.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Practical impact: Helps reduce food waste by suggesting meals from what you already own.&lt;/li&gt;
&lt;li&gt;User delight: A fun, interactive way to explore cooking ideas.&lt;/li&gt;
&lt;li&gt;Strong multimodal showcase: Combines text understanding, recipe creation, and visual generation in a single workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;p&gt;Solo submission by &lt;a class="mentioned-user" href="https://dev.to/devrayat000"&gt;@devrayat000&lt;/a&gt; &lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Hairstyle AI Try-On ✂️🤖</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Sun, 14 Sep 2025 19:55:25 +0000</pubDate>
      <link>https://dev.to/devrayat000/hairstyle-ai-try-on-4p0e</link>
      <guid>https://dev.to/devrayat000/hairstyle-ai-try-on-4p0e</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;What if, before getting a haircut, you could actually see how you’d look?&lt;br&gt;
&lt;strong&gt;Hairstyle AI Try-On&lt;/strong&gt; lets you upload your photo and instantly preview multiple hairstyles, complete with AI-generated ratings to help you find your perfect match.&lt;/p&gt;

&lt;p&gt;This app solves a very real problem: most of us take a risk when we try a new hairstyle. By using multimodal AI, you can confidently test different styles virtually before visiting the barber.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 &lt;a href="https://hairstyle-ai-try-on-441360358721.us-west1.run.app" rel="noopener noreferrer"&gt;Live Demo Link&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;Initial State (upload your photo):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xj1q5kg94ytune9h0rt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xj1q5kg94ytune9h0rt.png" alt="Initial"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgda20z56mrsqdd0pmzro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgda20z56mrsqdd0pmzro.png" alt="Add Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecj9coee8k0488lgl9sh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecj9coee8k0488lgl9sh.png" alt="Start Processing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI-Generated Hairstyles:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftomcm9iv75bal1muopy1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftomcm9iv75bal1muopy1.png" alt="Hairstyle 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u58tfbdh5s0grmwkcy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u58tfbdh5s0grmwkcy0.png" alt="Hairstyle 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Google AI Studio
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I built and deployed the entire experience on &lt;strong&gt;Google AI Studio&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The backend runs on &lt;strong&gt;Cloud Run&lt;/strong&gt;, making it scalable and easy to deploy.&lt;/li&gt;
&lt;li&gt;Gemini was used for &lt;strong&gt;image understanding&lt;/strong&gt; (detecting the face and aligning hairstyles) and &lt;strong&gt;content generation&lt;/strong&gt; (evaluating &amp;amp; scoring hairstyles).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image Understanding&lt;/strong&gt;: The uploaded photo is analyzed to detect face features and properly overlay hairstyles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Generation/Transformation&lt;/strong&gt;: Hairstyles are applied virtually, with realistic blending to match lighting and head shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text + Scoring Output&lt;/strong&gt;: Gemini provides natural-language feedback and a numerical “style score” (e.g. 8.5/10 for Curly Fringe).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mix of visual + evaluative output makes the experience both fun and practical.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delightful user experience&lt;/strong&gt;: Try on hairstyles virtually, no risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practical application&lt;/strong&gt;: Helps people make confident styling decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shows multimodal power&lt;/strong&gt;: Combines &lt;strong&gt;vision, generation, and evaluation&lt;/strong&gt; in one workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Team
&lt;/h2&gt;

&lt;p&gt;Solo submission by &lt;a class="mentioned-user" href="https://dev.to/devrayat000"&gt;@devrayat000&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;This project demonstrates how Gemini’s multimodal capabilities can power consumer-friendly, real-world experiences. It’s fun, engaging, and practical — all in one.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>Highlighting Image Text</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Tue, 30 Apr 2024 22:10:00 +0000</pubDate>
      <link>https://dev.to/devrayat000/highlighting-image-text-4hhh</link>
      <guid>https://dev.to/devrayat000/highlighting-image-text-4hhh</guid>
      <description>&lt;p&gt;Image processing and data extraction has become one of the most powerful features of Machine Learning now. But doing it from scratch is a pain in the a**. The one thing programming taught me that no one else did is not to reinvent the wheel every time and to prioritize getting the job done. Keeping that in mind, I have come across an easy solution for the problem at hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem At Hand
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6fvubs2ftao8gfhjile.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6fvubs2ftao8gfhjile.png" alt="Raw image before highlighting" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For simplicity's sake, let us consider this to be a page from a book. We want to highlight the word &lt;em&gt;comment&lt;/em&gt; wherever it occurs. This could be an intuitive feature for image search engines to direct the users' attention to their desired content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;We are going to be using an OCR (Optical Character Recognition) engine called &lt;a href="https://tesseract-ocr.github.io"&gt;Tesseract&lt;/a&gt; for the image-to-text recognition part. It is free software, released under the Apache License. Install the engine for your desired OS from their official website. I'm using Windows for this. Add the installation path to your environment variables.&lt;/p&gt;

&lt;p&gt;Create a python project with a virtual environment set up on it. Install the necessary packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;opencv-python &lt;span class="c"&gt;# for image processing&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pytesseract &lt;span class="c"&gt;# to use the ocr engine in your project&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;pandas &lt;span class="c"&gt;# to conduct search queries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your main.py import the necessary libraries and define the necessary variables. Read the image from the source using the &lt;em&gt;imread&lt;/em&gt; method. Make a copy of the original image for the overlay. Extract text information from the image. It is important to set the &lt;em&gt;output_type&lt;/em&gt; to be a pandas Dataframe object which will ease the filtering process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pytesseract&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytesseract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt;

&lt;span class="n"&gt;ALPHA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;

&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;devto.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;comment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# make a copy of the original image for the highlight overlay
&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# extract text data from the image as a pandas Dataframe object
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pytesseract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image_to_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ben+eng&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DATAFRAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dataframe object returned has the following structure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;level&lt;/th&gt;
&lt;th&gt;page_num&lt;/th&gt;
&lt;th&gt;block_num&lt;/th&gt;
&lt;th&gt;par_num&lt;/th&gt;
&lt;th&gt;line_num&lt;/th&gt;
&lt;th&gt;word_num&lt;/th&gt;
&lt;th&gt;left&lt;/th&gt;
&lt;th&gt;top&lt;/th&gt;
&lt;th&gt;width&lt;/th&gt;
&lt;th&gt;height&lt;/th&gt;
&lt;th&gt;conf&lt;/th&gt;
&lt;th&gt;text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;169&lt;/td&gt;
&lt;td&gt;537&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;96.276794&lt;/td&gt;
&lt;td&gt;comments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We are only interested in the &lt;em&gt;text&lt;/em&gt;, &lt;em&gt;left&lt;/em&gt;, &lt;em&gt;top&lt;/em&gt;, &lt;em&gt;width&lt;/em&gt;, and &lt;em&gt;height&lt;/em&gt; columns. We need to prepare the dataframe for this specific job by applying various filters. Drop the rows that have &lt;em&gt;NaN&lt;/em&gt; or empty string in the &lt;em&gt;text&lt;/em&gt; column to make our data error-proof and the computations more efficient. The text column usually contains single words. We can iterate through each row to find out if any of them matches our query string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# drop rows that have NaN values in the text column
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dropna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# remove empty text rows
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Search through the text column for matching words
&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can get started with the highlighting part. We will draw rectangular highlight boxes around the matched positions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iterrows&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;width&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;height&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# draw a yellow rectangle around the matched text
&lt;/span&gt;    &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add the overlay on the original image
&lt;/span&gt;&lt;span class="n"&gt;img_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addWeighted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ALPHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ALPHA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Some more image processing to make the highlights more realistic
&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;img_new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;dim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;resized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;interpolation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INTER_AREA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Show the modified image using opencv's &lt;em&gt;imshow&lt;/em&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Highlighted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is this modified image with every occurring &lt;em&gt;comment&lt;/em&gt; highlighted in yellow.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaftpbrka1vur4y8l8rm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feaftpbrka1vur4y8l8rm.jpg" alt="Highlighted image" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Bonus Tip
&lt;/h2&gt;

&lt;p&gt;The search-through mechanism in this process can only detect and highlight a single word or full sentence with exact matches. If we want to highlight words that are not in a single sentence, we just need to filter the dataframe with a little bit of pandas magic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;boxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;boxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;case&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, the user can query "essential comments" and it will highlight &lt;em&gt;essential&lt;/em&gt; and &lt;em&gt;comments&lt;/em&gt; even though they are not together.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opencv</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Hash routing in Remix!</title>
      <dc:creator>Zul Ikram Musaddik Rayat</dc:creator>
      <pubDate>Fri, 29 Jul 2022 15:09:58 +0000</pubDate>
      <link>https://dev.to/devrayat000/hash-routing-in-remix-e2p</link>
      <guid>https://dev.to/devrayat000/hash-routing-in-remix-e2p</guid>
      <description>&lt;p&gt;&lt;a href="https://remix.run/"&gt;Remix&lt;/a&gt; is the newest hottest full-stack React framework. Remix supports file-based routing which uses &lt;code&gt;react-router-dom&lt;/code&gt; under the hood which is a popular react routing library.&lt;/p&gt;

&lt;p&gt;Because Remix users react-router's &lt;em&gt;BrowserRouter&lt;/em&gt; internally, you can't actually do &lt;strong&gt;Hash Routing&lt;/strong&gt; (id based routing that triggers scrolling) with it. &lt;/p&gt;

&lt;p&gt;For example, if you create a link like this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#footer"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Go to bottom
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it will surely change the browser URL, but won't scroll down to the element with an id of footer.&lt;/p&gt;

&lt;p&gt;Of course, there's an easier way to achieve this behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning off Client-Side Routing
&lt;/h2&gt;

&lt;p&gt;We can add a &lt;code&gt;reloadDocument&lt;/code&gt; prop to the specialized &lt;strong&gt;Link&lt;/strong&gt; component and it will start acting like a normal anchor tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#footer"&lt;/span&gt; &lt;span class="na"&gt;reloadDocument&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Go to bottom
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This in turns, turns off client-side routing and lets the browser handle the transition, as &lt;a href="https://reactrouter.com/docs/en/v6/components/link"&gt;mentioned here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is fine until you want both client-side routing and scroll behavior on hash routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual Scrolling via Side-Effect
&lt;/h2&gt;

&lt;p&gt;The workaround this is to catch the side-effect created by the route change inside a &lt;code&gt;useEffect&lt;/code&gt; and handle the scrolling manually.&lt;/p&gt;

&lt;p&gt;In the root (&lt;em&gt;root.jsx&lt;/em&gt; file) of our projects, we have to get the &lt;em&gt;location&lt;/em&gt; object from the &lt;code&gt;useLocation&lt;/code&gt; hook provided by remix. Then, we have to create a &lt;em&gt;useEffect&lt;/em&gt; which depends on that location object. The &lt;em&gt;location&lt;/em&gt; object has a property called &lt;em&gt;hash&lt;/em&gt; which provides us with the hash portion of the URL e.g. &lt;strong&gt;#footer&lt;/strong&gt; if the URL is &lt;strong&gt;&lt;a href="http://www.example.com/#footer"&gt;www.example.com/#footer&lt;/a&gt;&lt;/strong&gt;. Then we can look up the element containing that id and manually scroll down to it using the &lt;code&gt;scrollIntoView&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLocation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
