<?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: Bro Karim</title>
    <description>The latest articles on DEV Community by Bro Karim (@brokarim).</description>
    <link>https://dev.to/brokarim</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%2F1129438%2Fcf40ce28-c709-4077-96f4-3a38352ff549.png</url>
      <title>DEV Community: Bro Karim</title>
      <link>https://dev.to/brokarim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brokarim"/>
    <language>en</language>
    <item>
      <title>animation</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Tue, 21 Jan 2025 04:11:16 +0000</pubDate>
      <link>https://dev.to/brokarim/animation-3k7a</link>
      <guid>https://dev.to/brokarim/animation-3k7a</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/brokarim" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F1129438%2Fcf40ce28-c709-4077-96f4-3a38352ff549.png" alt="brokarim"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/brokarim/animated-footer-w-float-icon-using-typescript-and-tailwind-1ioa" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Animated Footer w/ Float Icon using typescript and tailwind&lt;/h2&gt;
      &lt;h3&gt;Bro Karim ・ Jan 21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>animation</category>
      <category>typescript</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Animated Footer w/ Float Icon using typescript and tailwind</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Tue, 21 Jan 2025 04:10:31 +0000</pubDate>
      <link>https://dev.to/brokarim/animated-footer-w-float-icon-using-typescript-and-tailwind-1ioa</link>
      <guid>https://dev.to/brokarim/animated-footer-w-float-icon-using-typescript-and-tailwind-1ioa</guid>
      <description>&lt;p&gt;A footer component featuring social media icons that float upward with a rotation effect when hovered, revealing their respective platform names underneath.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1881554286825689489-655" src="https://platform.twitter.com/embed/Tweet.html?id=1881554286825689489"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1881554286825689489-655');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1881554286825689489&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;footer-animation.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Send } from "lucide-react";

export function Footer() {
  return (
    &amp;lt;&amp;gt;
        &amp;lt;footer className="w-full mx-auto container max-w-7xl rounded-2xl px-4 h-16 flex items-center py-3 bg-gray-100"&amp;gt;
          &amp;lt;div className="w-full  flex justify-between items-center"&amp;gt;
            {/* Email section */}
            &amp;lt;div className="flex items-center gap-2"&amp;gt;
              &amp;lt;Send className="size-7" /&amp;gt;
              &amp;lt;Link href="mailto:hello@danielsun.space" className="text-2xl hover:underline"&amp;gt;
                hello@brokariim.space
              &amp;lt;/Link&amp;gt;
            &amp;lt;/div&amp;gt;

            {/* Social icons */}
            &amp;lt;div className="flex items-center relative"&amp;gt;
              &amp;lt;Link href="#" className="relative group grid place-items-center"&amp;gt;
                &amp;lt;div className=" rotate-6 group-hover:-rotate-12 group-hover:-translate-y-6 transition duration-500 hover:duration-200 rounded-xl grid place-items-center"&amp;gt;
                  &amp;lt;img src="/wa3d.png" alt="" className="w-16 h-16" /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;span className="absolute top-10  opacity-0 group-hover:opacity-100 transition-opacity duration-300 text-[10px] font-medium"&amp;gt;Whatsapp&amp;lt;/span&amp;gt;
              &amp;lt;/Link&amp;gt;
              &amp;lt;Link href="#" className="relative group grid place-items-center"&amp;gt;
                &amp;lt;div className=" -rotate-6 group-hover:-rotate-12 group-hover:-translate-y-6 transition duration-500 hover:duration-200 rounded-xl grid place-items-center"&amp;gt;
                  &amp;lt;img src="/ig3d.png" alt="" className="w-16 h-16" /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;span className="absolute top-10  opacity-0 group-hover:opacity-100 transition-opacity duration-300 text-[10px] font-medium"&amp;gt;Instagram&amp;lt;/span&amp;gt;
              &amp;lt;/Link&amp;gt;
              &amp;lt;Link href="#" className="relative group grid place-items-center"&amp;gt;
                &amp;lt;div className=" rotate-6 group-hover:-rotate-12 group-hover:-translate-y-6 transition duration-500 hover:duration-200 rounded-xl grid place-items-center"&amp;gt;
                  &amp;lt;img src="/dc3d.png" alt="" className="w-16 h-16" /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;span className="absolute top-10  opacity-0 group-hover:opacity-100 transition-opacity duration-300 text-[10px] font-medium"&amp;gt;Discord&amp;lt;/span&amp;gt;
              &amp;lt;/Link&amp;gt;
              &amp;lt;Link href="#" className="relative group grid place-items-center"&amp;gt;
                &amp;lt;div className=" -rotate-6 group-hover:-rotate-12 group-hover:-translate-y-6 transition duration-500 hover:duration-200 rounded-xl grid place-items-center"&amp;gt;
                  &amp;lt;img src="/ln3d.png" alt="" className="w-16 h-16" /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;span className="absolute top-10  opacity-0 group-hover:opacity-100 transition-opacity duration-300 text-[10px] font-medium"&amp;gt;Linkdln&amp;lt;/span&amp;gt;
              &amp;lt;/Link&amp;gt;
              &amp;lt;Link href="#" className="relative group grid place-items-center"&amp;gt;
                &amp;lt;div className=" rotate-6 group-hover:-rotate-12 group-hover:-translate-y-6 transition duration-500 hover:duration-200 rounded-xl grid place-items-center"&amp;gt;
                  &amp;lt;img src="/tiktok3d.png" alt="" className="w-16 h-16" /&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;span className="absolute top-10  opacity-0 group-hover:opacity-100 transition-opacity duration-300 text-[10px] font-medium"&amp;gt;Tiktok&amp;lt;/span&amp;gt;
              &amp;lt;/Link&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/footer&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Credit
&lt;/h2&gt;

&lt;p&gt;•⁠  ⁠3d icon : &lt;a href="https://www.figma.com/community/file/1015485067395949509" rel="noopener noreferrer"&gt;https://www.figma.com/community/file/1015485067395949509&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Animated Work Card using Typescript, Tailwind and Framer Motion</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Sat, 18 Jan 2025 14:41:20 +0000</pubDate>
      <link>https://dev.to/brokarim/animated-work-card-using-typescript-tailwind-and-framer-motion-58n1</link>
      <guid>https://dev.to/brokarim/animated-work-card-using-typescript-tailwind-and-framer-motion-58n1</guid>
      <description>&lt;p&gt;Simple work card components that brings your projects to life. As users hover over the card, a subtle gradient fades in from the top corner while the image tilts slightly, creating a dynamic and engaging effect.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1880625972871049553-874" src="https://platform.twitter.com/embed/Tweet.html?id=1880625972871049553"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1880625972871049553-874');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1880625972871049553&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;I used several UI libraries like shadcn for the &lt;code&gt;card.tsx&lt;/code&gt; and &lt;a href="https://21st.dev/mikolajdobrucki/cta-with-glow/default" rel="noopener noreferrer"&gt;21st.dev&lt;/a&gt; for the &lt;code&gt;Glow.tsx&lt;/code&gt; component. If you're curious about the glow effect, check it out on &lt;a href="https://21st.dev/mikolajdobrucki/cta-with-glow/default" rel="noopener noreferrer"&gt;21st.dev&lt;/a&gt;—it's a must-see!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;work-card.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;Card className="group relative w-[800px] rounded-3xl p-8 border-none overflow-hidden bg-[#f5f5f5] mb-10"&amp;gt;
          &amp;lt;Glow variant="top" className="group-hover:opacity-100 opacity-0" /&amp;gt;
          &amp;lt;div className="relative z-10"&amp;gt;
            {/* Logo and Title */}
            &amp;lt;div className="flex items-center gap-2 mb-4"&amp;gt;
              &amp;lt;div className="w-8 h-8 bg-black rounded-lg flex items-center justify-center"&amp;gt;
                &amp;lt;svg viewBox="0 0 24 24" className="w-5 h-5 text-white" fill="none" stroke="currentColor"&amp;gt;
                  &amp;lt;path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /&amp;gt;
                &amp;lt;/svg&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;span className="text-2xl font-semibold"&amp;gt;Simple&amp;lt;/span&amp;gt;
            &amp;lt;/div&amp;gt;

            {/* Description */}
            &amp;lt;p className="text-gray-600 text-lg mb-8 max-w-2xl"&amp;gt;From classic elegance to moddern innovation, this watch are crafted to elevate your style .&amp;lt;/p&amp;gt;

            {/* Images Container */}
            &amp;lt;div className="flex w-full relative  justify-center gap-2 items-center transition-all duration-500 ease-in-out group-hover:translate-y-2"&amp;gt;
              &amp;lt;div className=" w-2/5 aspect-[4/3] -rotate-6 grid place-items-center -bottom-28 border-4 border-white rounded-xl relative   shadow-lg ring-1 ring-border group-hover:-translate-x-5 group-hover:-rotate-12 group-hover:-translate-y-0.5 transition duration-500 group-hover:duration-200"&amp;gt;
                &amp;lt;img
                  src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?q=80&amp;amp;w=2899&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;ixlib=rb-4.0.3&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
                  alt="Rachit Tank via unsplash"
                  className="w-full h-full object-cover rounded-xl"
                /&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;div className="w-1/2 aspect-[4/3] grid place-items-center -bottom-20 rounded-xl border-4 border-white relative right-2.5   shadow-lg ring-1 ring-border group-hover:translate-x-5 group-hover:rotate-12 group-hover:-translate-y-0.5 transition duration-500 group-hover:duration-200"&amp;gt;
                &amp;lt;img
                  src="https://images.unsplash.com/photo-1638095562082-449d8c5a47b4?q=80&amp;amp;w=2942&amp;amp;auto=format&amp;amp;fit=crop&amp;amp;ixlib=rb-4.0.3&amp;amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
                  alt="Howard Bouchevereau via unsplash"
                  className="w-full h-full object-cover rounded-xl border-4 border-white"
                /&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/Card&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Animated Select component using typescript, shadcn and framer-motion</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Wed, 15 Jan 2025 07:32:03 +0000</pubDate>
      <link>https://dev.to/brokarim/animated-select-component-using-typescript-shadcn-and-framer-motion-24pb</link>
      <guid>https://dev.to/brokarim/animated-select-component-using-typescript-shadcn-and-framer-motion-24pb</guid>
      <description>&lt;p&gt;The "Expand Select Animation" is a custom select component built using TypeScript and Framer Motion, with the base component provided by ShadCN. This component enhances the standard select element with a smooth, visually appealing animation that expands downward to reveal options and collapses upward to hide them.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1878086086502887693-992" src="https://platform.twitter.com/embed/Tweet.html?id=1878086086502887693"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1878086086502887693-992');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1878086086502887693&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;expand-select.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Globe } from "lucide-react";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { motion, AnimatePresence } from "framer-motion";

export function ExapandSelect() {
  return (
    &amp;lt;Select&amp;gt;
      &amp;lt;SelectTrigger className="text-white w-[180px]flex gap-2 bg-[#1a1a1a] hover:bg-[#3e3d3d] ring-none border-none "&amp;gt;
        &amp;lt;Globe /&amp;gt;
        &amp;lt;SelectValue placeholder="English" /&amp;gt;
      &amp;lt;/SelectTrigger&amp;gt;
      &amp;lt;AnimatePresence&amp;gt;
        &amp;lt;SelectContent className="bg-[#3e3d3d] text-white  border-none p-[1px]"&amp;gt;
          &amp;lt;motion.div
            initial={{ opacity: 0, height: 0, scale: 0.95 }}
            animate={{
              opacity: 1,
              height: "auto",
              scale: 1,
              transition: {
                type: "spring",
                stiffness: 300,
                damping: 30,
              },
            }}
            exit={{
              opacity: 0,
              height: 0,
              scale: 0.95,
              transition: {
                duration: 0.2,
              },
            }}
            style={{ transformOrigin: "center" }}
          &amp;gt;
            &amp;lt;SelectGroup&amp;gt;
              &amp;lt;SelectItem className=" focus:bg-[#1a1a1a] focus:text-white" value="eng"&amp;gt;
                English
              &amp;lt;/SelectItem&amp;gt;
              &amp;lt;SelectItem className=" focus:bg-[#1a1a1a] focus:text-white" value="france"&amp;gt;
                Français
              &amp;lt;/SelectItem&amp;gt;
              &amp;lt;SelectItem className=" focus:bg-[#1a1a1a] focus:text-white" value="spain"&amp;gt;
                Español
              &amp;lt;/SelectItem&amp;gt;
              &amp;lt;SelectItem className=" focus:bg-[#1a1a1a] focus:text-white" value="deutsch"&amp;gt;
                Deutsch
              &amp;lt;/SelectItem&amp;gt;
              &amp;lt;SelectItem className=" focus:bg-[#1a1a1a] focus:text-white" value="china"&amp;gt;
                中国
              &amp;lt;/SelectItem&amp;gt;
            &amp;lt;/SelectGroup&amp;gt;
          &amp;lt;/motion.div&amp;gt;
        &amp;lt;/SelectContent&amp;gt;
      &amp;lt;/AnimatePresence&amp;gt;
    &amp;lt;/Select&amp;gt;
  );
}

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

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>Animated Hover Logo using Typescript, Tailwind and Framer Motion</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Mon, 13 Jan 2025 10:19:24 +0000</pubDate>
      <link>https://dev.to/brokarim/animated-hover-logo-using-typescript-tailwind-and-framer-motion-2732</link>
      <guid>https://dev.to/brokarim/animated-hover-logo-using-typescript-tailwind-and-framer-motion-2732</guid>
      <description>&lt;p&gt;It transforms a static logo into a button, which activates a video tooltip that follows the cursor's movement along the button's axis using motion values.&lt;/p&gt;

&lt;p&gt;Upon closer inspection, you'll notice we use micro-animations. When the logo is hovered over, it smoothly scales down and fades out, then transitions into a button that scales up with a fade-in effect. Hovering over the button then triggers a video tooltip.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1878748540677283930-172" src="https://platform.twitter.com/embed/Tweet.html?id=1878748540677283930"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1878748540677283930-172');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1878748540677283930&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;logo-hover.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import { useState, useCallback } from "react";
import { motion, AnimatePresence, useMotionValue } from "framer-motion";
import { Play } from "lucide-react";

export default function LogoHover() {
  const [isHovered, setIsHovered] = useState(false);
  const [showVideo, setShowVideo] = useState(false);
  const x = useMotionValue(0);

  const handleMouseMove = useCallback(
    (event: React.MouseEvent&amp;lt;HTMLElement&amp;gt;) =&amp;gt; {
      const halfWidth = event.currentTarget.offsetWidth / 2;
      x.set(event.nativeEvent.offsetX - halfWidth);
    },
    [x]
  );

  return (
    &amp;lt;div className="relative w-[100px] h-[80px] cursor-pointer" onMouseEnter={() =&amp;gt; setIsHovered(true)} onMouseLeave={() =&amp;gt; setIsHovered(false)}&amp;gt;
      &amp;lt;AnimatePresence&amp;gt;
        {!isHovered &amp;amp;&amp;amp; (
          // change with your own logo
          &amp;lt;motion.img src="/theo-logo.png" alt="Wegic Logo" className="absolute inset-0 w-full h-full object-contain" initial={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.2 }} /&amp;gt;
        )}
      &amp;lt;/AnimatePresence&amp;gt;

      &amp;lt;AnimatePresence&amp;gt;
        {isHovered &amp;amp;&amp;amp; (
          &amp;lt;motion.div className="absolute inset-0 flex items-center justify-center w-full" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.2 }}&amp;gt;
            &amp;lt;button
              className="flex w-full items-center justify-center  gap-2 bg-white border border-black h-10  py-2 rounded-md"
              onMouseMove={handleMouseMove}
              onMouseEnter={() =&amp;gt; setShowVideo(true)}
              onMouseLeave={() =&amp;gt; setShowVideo(false)}
            &amp;gt;
              &amp;lt;Play size={16} fill="white" strokeWidth={2} aria-hidden="true" /&amp;gt;
              &amp;lt;p className="text-[10px] flex font-semibold"&amp;gt;watch video&amp;lt;/p&amp;gt;
            &amp;lt;/button&amp;gt;
          &amp;lt;/motion.div&amp;gt;
        )}
      &amp;lt;/AnimatePresence&amp;gt;
      {/* Video Tooltip */}
      &amp;lt;AnimatePresence mode="popLayout"&amp;gt;
        {showVideo &amp;amp;&amp;amp; (
          &amp;lt;motion.div
            initial={{ opacity: 0, y: 20, scale: 0.6 }}
            animate={{
              opacity: 1,
              y: 0,
              scale: 1,
              transition: {
                stiffness: 260,
                damping: 10,
                duration: 0.3,
              },
              width: "200px",
              height: "auto",
            }}
            exit={{ opacity: 0, y: 20, scale: 0.6 }}
            style={{
              translateX: x,
            }}
            className="absolute top-full mt-2 z-50"
          &amp;gt;
            &amp;lt;motion.div className="rounded-md overflow-hidden "&amp;gt;
              &amp;lt;iframe
                width="560"
                height="315"
                src="https://www.youtube.com/embed/ZK-rNEhJIDs?si=KBECTf4W-b_37Xsn&amp;amp;amp;autoplay=1&amp;amp;mute=1&amp;amp;controls=0"
                title="YouTube video player"
                className="w-full h-full object-cover rounded-md border-[1px] border-white ring-1 ring-black/5"
                allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                allowFullScreen
              &amp;gt;&amp;lt;/iframe&amp;gt;
            &amp;lt;/motion.div&amp;gt;
          &amp;lt;/motion.div&amp;gt;
        )}
      &amp;lt;/AnimatePresence&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}


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

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Animated Video Tooltip using typescript and framer-motion</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Mon, 06 Jan 2025 14:38:07 +0000</pubDate>
      <link>https://dev.to/brokarim/animated-video-tooltip-using-typescript-and-framer-motion-25c8</link>
      <guid>https://dev.to/brokarim/animated-video-tooltip-using-typescript-and-framer-motion-25c8</guid>
      <description>&lt;p&gt;Video Tooltip is an animated component that activates when users hover over an avatar. &lt;/p&gt;

&lt;p&gt;This component displays a short video of the person introducing themselves or providing additional context, adding a personal and interactive touch. &lt;/p&gt;

&lt;p&gt;It's particularly useful for creating memorable user experiences, offering quick insights about team members, speakers, or influencers without requiring extra clicks.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1876277733086707986-544" src="https://platform.twitter.com/embed/Tweet.html?id=1876277733086707986"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1876277733086707986-544');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1876277733086707986&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;video-tooltip.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState, useCallback, useMemo } from "react";
import { motion, useTransform, AnimatePresence, useMotionValue, useSpring } from "framer-motion";
import { cn } from "@/lib/utils";

interface TooltipItem {
  id: number;
  name: string;
  designation: string;
  image: string;
  video: string;
  text: string;
}

interface VideoTooltipProps {
  items: TooltipItem[];
  className?: string;
}

export const VideoTooltip = ({ items, className = "" }: VideoTooltipProps) =&amp;gt; {
  const [hoveredIndex, setHoveredIndex] = useState&amp;lt;number | null&amp;gt;(null);
  const [showText, setShowText] = useState(false);
  const springConfig = useMemo(
    () =&amp;gt; ({
      stiffness: 100,
      damping: 5,
    }),
    []
  );

  // Motion setup
  const x = useMotionValue(0);
  const translateX = useSpring(useTransform(x, [-100, 100], [-50, 50]), springConfig);
  // Optimize event handler with useCallback
  const handleMouseMove = useCallback(
    (event: React.MouseEvent&amp;lt;HTMLElement&amp;gt;) =&amp;gt; {
      const halfWidth = event.currentTarget.offsetWidth / 2;
      x.set(event.nativeEvent.offsetX - halfWidth);
    },
    [x]
  );

  return (
    &amp;lt;div className={cn("flex items-center gap-2", className)}&amp;gt;
      {items.map((item) =&amp;gt; (
        &amp;lt;div className="-mr-4 relative group" key={item.name} onMouseEnter={() =&amp;gt; setHoveredIndex(item.id)} onMouseLeave={() =&amp;gt; setHoveredIndex(null)}&amp;gt;
          &amp;lt;AnimatePresence mode="popLayout"&amp;gt;
            {hoveredIndex === item.id &amp;amp;&amp;amp; (
              &amp;lt;motion.div
                initial={{ opacity: 0, y: 20, scale: 0.6 }}
                animate={{
                  opacity: 1,
                  y: 0,
                  scale: 1,
                  transition: {
                    stiffness: 260,
                    damping: 10,
                    duration: 0.3,
                  },
                  width: showText ? "300px" : "96px",
                  height: showText ? "auto" : "96px",
                }}
                exit={{ opacity: 0, y: 20, scale: 0.6 }}
                style={{
                  translateX: translateX,
                  // rotate: rotate,
                  // whiteSpace: "nowrap",
                }}
                className="absolute w-24 h-24 group  -top-28 -left-1/2 translate-x-1/2 border-2 border-white flex text-xs flex-col bg-white items-center justify-center rounded-md  z-50 shadow-xl px-4 py-2"
              &amp;gt;
                &amp;lt;motion.div animate={{ opacity: showText ? 0 : 1 }} transition={{ duration: 0.3 }} className="absolute inset-0 z-10"&amp;gt;
                  &amp;lt;video src={item.video} autoPlay muted loop playsInline className="w-full h-full object-cover rounded-md ring-black" /&amp;gt;
                &amp;lt;/motion.div&amp;gt;
                &amp;lt;motion.div className="p-1 w-full bg-white max-h-32 overflow-y-auto flex  flex-col" initial={{ opacity: 0 }} animate={{ opacity: showText ? 1 : 0 }} transition={{ duration: 0.3 }}&amp;gt;
                  &amp;lt;p className="text-sm text-black text-foreground-foreground"&amp;gt;{item.text}&amp;lt;/p&amp;gt;
                &amp;lt;/motion.div&amp;gt;
                &amp;lt;div className=" relative h-full w-full "&amp;gt;
                  &amp;lt;div className={`absolute ${showText ? "left-0" : "-left-[16%]"} bottom-2 flex space-x-2 z-30 items-center justify-center rounded-full border border-white text-black p-1`}&amp;gt;
                    &amp;lt;button className={`text-[8px] h-auto rounded-full px-1 ${!showText ? "bg-black text-white" : ""}`} onClick={() =&amp;gt; setShowText(false)}&amp;gt;
                      Video
                    &amp;lt;/button&amp;gt;
                    &amp;lt;button className={`text-[8px] h-auto rounded-full px-1 ${showText ? "bg-black text-white" : ""}`} onClick={() =&amp;gt; setShowText(true)}&amp;gt;
                      Text
                    &amp;lt;/button&amp;gt;
                  &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/motion.div&amp;gt;
            )}
          &amp;lt;/AnimatePresence&amp;gt;
          &amp;lt;img
            onMouseMove={handleMouseMove}
            height={100}
            width={100}
            src={item.image}
            alt={item.name}
            className="object-cover !m-0 !p-0 object-top rounded-full h-14 w-14 border-2 group-hover:scale-105 group-hover:z-30 border-background relative transition duration-500"
          /&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  );
};

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

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Handling Local Storage for Toaster Notifications</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Thu, 02 Jan 2025 13:58:16 +0000</pubDate>
      <link>https://dev.to/brokarim/handling-local-storage-for-toaster-notifications-fg0</link>
      <guid>https://dev.to/brokarim/handling-local-storage-for-toaster-notifications-fg0</guid>
      <description>&lt;p&gt;Demo : &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1874817943860834535-693" src="https://platform.twitter.com/embed/Tweet.html?id=1874817943860834535"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1874817943860834535-693');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1874817943860834535&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Source code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;============= layout.tsx 
&amp;lt;body&amp;gt;
   &amp;lt;ThemeProvider attribute="class" defaultTheme="dark"&amp;gt;
     {children}
      &amp;lt;DevToaster /&amp;gt;
   &amp;lt;/ThemeProvider&amp;gt;
&amp;lt;/body&amp;gt;

============= dev-toaster.tsx 
export function TemplateToaster() {
  const [isVisibile, setIsVisible] = useState(true);
  const [notificationStatus, setNotificationStatus] = useState&amp;lt;string | null&amp;gt;(null);
  const path = usePathname();
  const [hydrated, setHydrated] = useState(false);

  // Run this effect only once to set hydrated to true
  useEffect(() =&amp;gt; {
    setHydrated(true);
  }, []);

  // Run this effect only on client side
  useEffect(() =&amp;gt; {
    if (hydrated) {
      // Remove the saved notification status to reset it
      localStorage.removeItem("behindui-notification");
      // Check localStorage after clearing
      setNotificationStatus(localStorage.getItem("behindui-notification"));
    }
  }, [hydrated]);

  if (!hydrated) {
    return null;
  }

  const templates = path.includes("templates");
  const isIframe = isInIframe();
  if (isIframe || notificationStatus === "off") {
    return &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;;
  }

  return (
    isVisibile &amp;amp;&amp;amp;
    !templates &amp;amp;&amp;amp; (
      &amp;lt;section className="relative z-50"&amp;gt;
        &amp;lt;div className="font-geist fixed bottom-4 right-4"&amp;gt;
          &amp;lt;Card className={cn(" w-[350px] bg-background [border:1px_solid_rgba(255,255,255,.1)]")}&amp;gt;
            &amp;lt;CardHeader&amp;gt;
              &amp;lt;div className="font-mono font-bold  uppercase tracking-tight"&amp;gt;
                🚧Website Under Development 🚧 &amp;lt;br /&amp;gt;
              &amp;lt;/div&amp;gt;
              &amp;lt;CardDescription className="mt-4 text-black/90 dark:text-white/70"&amp;gt;This website is currently in active development. Some features may be incomplete or not working as expected&amp;lt;/CardDescription&amp;gt;
            &amp;lt;/CardHeader&amp;gt;
            &amp;lt;CardFooter className="flex justify-end gap-4"&amp;gt;
              &amp;lt;Button
                size="sm"
                onClick={() =&amp;gt; {
                  localStorage.setItem("behindui-notification", "off");
                  setIsVisible(false);
                }}
              &amp;gt;
                Understand
              &amp;lt;/Button&amp;gt;
            &amp;lt;/CardFooter&amp;gt;
          &amp;lt;/Card&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/section&amp;gt;
    )
  );
}

function isInIframe() {
  try {
    return window.self !== window.top;
  } catch (e) {
    return true;
  }
}

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

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>shadcn</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Journey In Learning Pandas</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Fri, 08 Nov 2024 02:11:15 +0000</pubDate>
      <link>https://dev.to/brokarim/list-source-to-learn-python-pandas-1f90</link>
      <guid>https://dev.to/brokarim/list-source-to-learn-python-pandas-1f90</guid>
      <description>&lt;p&gt;This is note a course or tutorial, this is just my own note that i create while learning python3&lt;/p&gt;

&lt;p&gt;## Course i use (it's free)&lt;br&gt;
firts of all, i am not using any course to learn only this usefull website : datawars.io&lt;/p&gt;
&lt;h2&gt;
  
  
  Install Jupyter
&lt;/h2&gt;

&lt;p&gt;But before i am learn in there, i already know therea soeme tools i need to install in my device link python3 and jupyter notebook. since i am using macbook i follow this tutorial &lt;br&gt;
&lt;a href="https://youtu.be/pkjtbnsX7Yw?si=kq85ZuhM1wLYle8S" rel="noopener noreferrer"&gt;https://youtu.be/pkjtbnsX7Yw?si=kq85ZuhM1wLYle8S&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;point penting &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A virtual environment is a self-contained directory that contains its own Python installation, along with its own set of installed packages. This is useful for managing dependencies and keeping your projects isolated from one another.&lt;/li&gt;
&lt;li&gt;Running the command &lt;code&gt;source bin/activate&lt;/code&gt; is the next step to activate your virtual environment&lt;/li&gt;
&lt;li&gt;So whenever you want to start jupyter notebook, you must go to the fodlder, run &lt;code&gt;source bin/activate&lt;/code&gt; than &lt;code&gt;jupyter notebook&lt;/code&gt; at leasth this step in macbook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;point penting &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jupyter is cell-based&lt;/li&gt;
&lt;li&gt;It's free and open source software. Anybody can inspect the source code, modify it and use it for free&lt;/li&gt;
&lt;li&gt;It's language agnostic. You can use Python, R, Julia or pretty much any other language&lt;/li&gt;
&lt;li&gt;It's web based, which means it can run on any browser&lt;/li&gt;
&lt;li&gt;It's a mature project with a huge community.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  second tool : visidata
&lt;/h2&gt;

&lt;p&gt;VisiData is an interactive multitool for tabular data. It combines the clarity of a spreadsheet, the efficiency of the terminal, and the power of Python, into a lightweight utility which can handle millions of rows with ease.&lt;/p&gt;

&lt;p&gt;Its hellp you help to explore, clean, edit, and restructure tabular data directly from the terminal. It supports a wide range of data formats, including CSV files, Excel spreadsheets, SQL databases, and more. With VisiData, you can interactively select, filter, and group rows, rearrange and transform columns, and even create ad-hoc data pipelines. Its intuitive interface and extensive keyboard shortcuts make it an efficient tool for data analysis and manipulation.&lt;/p&gt;

&lt;p&gt;Installation &amp;amp;  more : &lt;a href="https://www.visidata.org/docs/" rel="noopener noreferrer"&gt;https://www.visidata.org/docs/&lt;/a&gt;&lt;br&gt;
To run the visidata, just typer :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vd [options] [input ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Intro to Pandas
&lt;/h2&gt;

&lt;p&gt;Pandas is our swiss knife when it comes to Data Analysis/Science in Python. We use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load/dump read/write data: to and from different formats (CSV, XML, HTML, Excel, JSON, even from the Internet)&lt;/li&gt;
&lt;li&gt;Analyze data: perform statistical analysis, query the data, find inconsistencies, etc&lt;/li&gt;
&lt;li&gt;Data cleaning: finding missing values, duplicate data, invalid or broken values, etc&lt;/li&gt;
&lt;li&gt;Visualizations: with support from matplotlib, we can quickly visualize data&lt;/li&gt;
&lt;li&gt;Data Wrangling/Munging: a non-so-scientific term that involves data handling: merging multiple data sources, creating derived representations, grouping data, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other resuorce that help me much to learn pandas is this repo : &lt;a href="https://github.com/TirendazAcademy/PANDAS-TUTORIAL/tree/main" rel="noopener noreferrer"&gt;https://github.com/TirendazAcademy/PANDAS-TUTORIAL/tree/main&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Docker Compose in Action : A Real Case Study for Nextjs Projects</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Sun, 08 Sep 2024 15:01:17 +0000</pubDate>
      <link>https://dev.to/brokarim/docker-compose-in-action-a-real-case-study-for-nextjs-projects-3h9p</link>
      <guid>https://dev.to/brokarim/docker-compose-in-action-a-real-case-study-for-nextjs-projects-3h9p</guid>
      <description>&lt;p&gt;Docker Compose sounds great in theory, but how does it actually look in a real project? In this post, we'll dive into a practical case study where I’ll walk you through the setup of my own Next.js application using Docker Compose. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2cxNmRwcW9oYzd0a3V1Nnd6d3IzdWhkbWFxMGY4NGN0bzV4a3ByNCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5wWf7H5pp15X4yOr2ik/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2cxNmRwcW9oYzd0a3V1Nnd6d3IzdWhkbWFxMGY4NGN0bzV4a3ByNCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5wWf7H5pp15X4yOr2ik/giphy.gif" width="450" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn't just another tutorial—this is the real deal. You’ll see exactly how I configured Redis, MySQL, and Next.js to work together in harmony using Docker. Spoiler alert: It’s easier than you think, and by the end, you’ll be ready to deploy your own stack with confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Journey Begins:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, there I was, diving into the world of open-source projects when I stumbled upon a repository that looked perfect for me. But there was one catch—it came with a &lt;code&gt;docker-compose.yml&lt;/code&gt; file, which meant I had to get my hands dirty with Docker Compose.&lt;/p&gt;

&lt;p&gt;Before we dive into the details of how everything works, let me show you the Docker Compose file I used to set up the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//docker-compose.yml
version: '2.3'

services:
  redis-server:
    restart: always
    image: redis:4.0
    platform: "linux/amd64"
    container_name: redis-server
    command: redis-server --requirepass ubuntu
    sysctls:
      - net.core.somaxconn=65535
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    mem_limit: 96m

  redis-insight:
    image: redislabs/redisinsight:latest
    platform: "linux/amd64"
    container_name: redis-insight
    ports:
      - "8001:8001"
    depends_on:
      - redis-server

  mysql-server:
    restart: always
    image: mysql:5.7
    container_name: mysql-server
    platform: "linux/amd64"
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      MYSQL_DATABASE: shiptalker
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    mem_limit: 512m

volumes:
  redis-data:
    driver: local
  mysql-data:
    driver: local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, we define three services—essentially, a &lt;a href="https://stackoverflow.com/questions/35565770/difference-between-service-and-container-in-docker-compose" rel="noopener noreferrer"&gt;service in Docker Compose&lt;/a&gt; refers to a &lt;strong&gt;containerized process&lt;/strong&gt;, such as a database or server, that your application depends on. these are the core components of our project that need to run. &lt;/p&gt;

&lt;p&gt;Here’s a quick overview of the services we’re using:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Redis Server&lt;/strong&gt;: This service is responsible for running a Redis instance, which acts as our in-memory data store, used for caching and real-time analytics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Insight&lt;/strong&gt;: This is a web-based GUI for managing and visualizing your Redis data. It provides an easier way to interact with Redis compared to the command line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL Server&lt;/strong&gt;: The database service where all the structured data (like user information) is stored.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's break it down step by step to see how this configuration powers the entire stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: MySQL – Setting the Foundation
&lt;/h2&gt;

&lt;p&gt;The first challenge on this journey was setting up MySQL. I opened up MySQL Workbench and created a  new database.&lt;/p&gt;

&lt;p&gt;Once the database was ready, the next step was to grab the all-important &lt;code&gt;DATABASE_URL&lt;/code&gt;. This key detail was needed in the project’s &lt;code&gt;env.js&lt;/code&gt; file, and without it, nothing would work.&lt;/p&gt;

&lt;p&gt;Since this is an open-source project, I need to follow their &lt;code&gt;DATABASE_URL&lt;/code&gt; format. It looks something like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql://youruser:yourpassword@localhost:3306/yourdatabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;his URL tells the application how to connect to your MySQL database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;youruser&lt;/code&gt;: The MySQL user (in this case, it's root by default).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yourpassword&lt;/code&gt;: The password for the MySQL user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;localhost&lt;/code&gt;: The host where MySQL is running.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3306&lt;/code&gt;: The default MySQL port.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yourdatabase&lt;/code&gt;: The database name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your MySQL database doesn't require a password, you can simply leave out the password, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql://youruser@localhost:3306/yourdatabase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify your MySQL user and port with the &lt;a href="https://stackoverflow.com/questions/4093603/how-do-i-find-out-my-mysql-url-host-port-and-username[](url)" rel="noopener noreferrer"&gt;following queries&lt;/a&gt; in the MySQL command line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To check the current user:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT user();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To check the port MySQL is running on:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SHOW VARIABLES WHERE Variable_name = 'port';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your DATABASE_URL is configured, the application will be able to connect to the MySQL database. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Setting Up Redis
&lt;/h2&gt;

&lt;p&gt;Now that MySQL was up and running, it was time to tackle the next step of the journey: setting up &lt;strong&gt;Redis&lt;/strong&gt;. But before we dive into the setup, let me explain what Redis is and why it's essential for this project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Redis is like the speedster of the data storage world. It’s an in-memory data structure store that acts as a lightning-fast cache. Think of it as the hyper-efficient assistant that quickly fetches and stores data, so your website doesn’t have to do the heavy lifting every single time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, I didn’t have Redis installed on my laptop yet, so that was the next task. If you're setting this up on &lt;strong&gt;Windows&lt;/strong&gt;, the official &lt;a href="https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-on-windows/" rel="noopener noreferrer"&gt;Redis documentation&lt;/a&gt; will guide you through the process. But here's the simplified version of what I did:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1️⃣Check Your Setup:&lt;/strong&gt; &lt;br&gt;
Make sure &lt;strong&gt;Ubuntu&lt;/strong&gt; and &lt;strong&gt;WSL 2&lt;/strong&gt; (Windows Subsystem for Linux) are installed and working on your laptop. &lt;br&gt;
Before we can install Redis on Windows, we need to enable the Windows Subsystem for Linux (WSL).&lt;/p&gt;

&lt;p&gt;WSL is a compatibility layer that enables running Linux binary executables natively on Windows 11 and Windows Server 2019.&lt;/p&gt;

&lt;p&gt;Open PowerShell as Administrator and run the following command to enable WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this command, you need to reboot your system. Note that this step only needs to be done once.&lt;/p&gt;

&lt;p&gt;Once the WSL is enabled, launch the Microsoft Windows Store and search for your preferred Linux distribution. In this example, we will use Ubuntu.&lt;/p&gt;

&lt;p&gt;Download the latest version of Ubuntu and wait for it to finish the installation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2️⃣Install Redis:&lt;/strong&gt; &lt;br&gt;
With Ubuntu and WSL 2 all set up, I jumped into the &lt;a href="https://www.windowscentral.com/essential-wsl-commands-powershell" rel="noopener noreferrer"&gt;Ubuntu terminal&lt;/a&gt; and ran the commands needed to install Redis. It’s a straightforward process :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3️⃣Configure and Start Redis:&lt;/strong&gt; &lt;br&gt;
After installation, I started the Redis server with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo service redis-server start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redis sprang to life, and just like that, I had a caching system up and running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4️⃣Connect to Redis:&lt;/strong&gt; &lt;br&gt;
Once Redis is running, you can test it by running redis-cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the connection with the ping command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; ping
PONG
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 : Install Redis Insight
&lt;/h2&gt;

&lt;p&gt;Redis Insight is a powerful tool for visualizing and optimizing data in Redis or Redis Stack, making real-time application development easier and more fun than ever before. Redis Insight lets you do both GUI- and CLI-based interactions in a fully-featured desktop GUI client.&lt;/p&gt;

&lt;p&gt;You can check out the installation &lt;a href="https://redis.io/docs/latest/develop/connect/insight/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It’s pretty straightforward—just head over to the Microsoft Store and download it. Easy peasy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Docker to the Rescue
&lt;/h2&gt;

&lt;p&gt;With &lt;strong&gt;MySQL&lt;/strong&gt; and &lt;strong&gt;Redis&lt;/strong&gt; set up individually, things were working, but let’s be real—it wasn’t the most convenient setup. I found myself constantly running different services manually, and that’s when I realized: &lt;strong&gt;Docker&lt;/strong&gt; could save me a lot of time and headaches. Instead of starting each service one by one, Docker allows us to run everything at once with the power of &lt;strong&gt;Docker Compose&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before we dive in, if you haven't set up Docker on your machine yet, don’t worry! I’ve created a detailed tutorial on how to install Docker👇🏻&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/brokarim/docker-on-windows-led-into-container-wonderland-3ppd"&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%2Fbnl6mqqe1f0ukeo0hqnx.gif" alt="Image description" width="572" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, with the docker dekstop and &lt;code&gt;docker-compose.yml&lt;/code&gt; ready, running all of these services together is simple. Here’s what you need to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Ubuntu&lt;/strong&gt;: Head over to your Ubuntu terminal (make sure you’re using WSL 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigate to the Directory&lt;/strong&gt;: Use &lt;code&gt;cd&lt;/code&gt; to move into the directory where your &lt;code&gt;docker-compose.yml&lt;/code&gt; file is located. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start the Services&lt;/strong&gt;: Run the following command to bring everything up:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command tells Docker to spin up all the services defined in your Compose file. It’s like hitting the power button for your project—everything just starts working!&lt;/p&gt;

&lt;p&gt;And that’s it! You don’t have to start Redis, MySQL, or Redis Insight individually anymore. Docker Compose does all the heavy lifting for you.&lt;/p&gt;




&lt;p&gt;So there you have it—your docker-compose setup is good to go!. If you found this blog post helpful, feel free to share it with others who might benefit from it. And hey, why not hit that follow button for more nerdy goodness on JavaScript, React, and all things web development?&lt;/p&gt;

&lt;p&gt;Let's stay in touch on &lt;a href="https://www.instagram.com/brokariim/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://x.com/BroKariim" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/BroKarim" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—where the real magic happens.&lt;/p&gt;

&lt;p&gt;Thanks for sticking around! 😊&lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>docker</category>
      <category>nextjs</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker on Windows: Led Into Container Wonderland</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Sun, 01 Sep 2024 04:00:46 +0000</pubDate>
      <link>https://dev.to/brokarim/docker-on-windows-led-into-container-wonderland-3ppd</link>
      <guid>https://dev.to/brokarim/docker-on-windows-led-into-container-wonderland-3ppd</guid>
      <description>&lt;p&gt;It all started with a simple curiosity. I came across this cool open-source project made with Next.js—something I was excited to explore. But then, hidden in the project files... the mysterious &lt;code&gt;docker-compose.yml&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMTI3dXpjYnI4eTJuaGx4c3RoMjg1OG90a3htMG91aHI0b2ozb3htaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/y208v1Dyxam7S/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMTI3dXpjYnI4eTJuaGx4c3RoMjg1OG90a3htMG91aHI0b2ozb3htaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/y208v1Dyxam7S/giphy.gif" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a quick Google search, I learned this wasn’t just some random file sitting there for decoration. No, no, it was essential! And to run this project, I needed Docker, the magical tool that everyone seemed to know about but never explained in simple terms.&lt;/p&gt;

&lt;p&gt;So, my journey began—installing Docker on Windows, navigating a few challenges along the way, and eventually learning that Docker is like packing your app into a shipping container, ready to sail smoothly across any platform. 🚢 But first, I had to figure out how to actually install the thing… and here’s how that went. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  Mission 1 : Install wsl
&lt;/h2&gt;

&lt;p&gt;So, the first stop on my Docker journey? WSL—aka, the &lt;em&gt;&lt;strong&gt;Windows Subsystem for Linux&lt;/strong&gt;&lt;/em&gt;. If you’re unfamiliar with WSL, think of it as a &lt;strong&gt;secret door&lt;/strong&gt; that lets you run a full Linux environment inside your Windows machine.&lt;/p&gt;

&lt;p&gt;I quickly learned that Docker plays really well with Linux, so installing WSL was my ticket to getting Docker up and running smoothly on Windows. My tool of choice? The mighty PowerShell! 🖥️ With one simple command, I summoned WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well, Windows will work its magic and install the default Linux distribution, which is Ubuntu! 🐧&lt;/p&gt;

&lt;p&gt;Now, this is where things got interesting. The first time Ubuntu launches, it asks for a username and password. In a flash of brilliance (or laziness), I decided to keep things simple: 'ubuntu' for both the username and password. &lt;/p&gt;

&lt;p&gt;After a brief moment, I found myself at the Ubuntu command line. But since we’re just getting started, the next step was to gracefully exit. To do that, I typed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and just like that, the Linux command line window closed.&lt;/p&gt;

&lt;p&gt;Now, if you’re feeling adventurous and want to try something other than Ubuntu, don’t worry—WSL gives you options! You can list all available distributions with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl -l -o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And switch to a different one by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --install -d &amp;lt;Distribution Name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But for now, we’ll stick with Ubuntu (I mean, I already committed to the username and password, right? 😆).&lt;/p&gt;

&lt;p&gt;Before we move forward, let’s make sure we’re using the right version of WSL. You can check which version you have installed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl -l -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’ve got &lt;code&gt;WSL 2&lt;/code&gt;, awesome! It’s faster, more powerful, and overall a better choice for Docker. Let’s make it the default by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --set-default-version 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, the first requirement for installing Docker is complete! We’re one step closer to containerized glory🚢.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mission 2 : Donwload docker
&lt;/h2&gt;

&lt;p&gt;With WSL set up and ready, it was time to tackle the next big mission: installing Docker itself. 🐳 But before diving headfirst into the download, I had to make sure my trusty device was up to the challenge. Docker doesn’t run on just any old machine, after all—it has a few requirements you’ll need to meet. &lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣Check the System Requirements
&lt;/h3&gt;

&lt;p&gt;First, I visited the official Docker website to grab the installer. But before hitting that download button, I double-checked that my device met Docker’s system requirements. &lt;/p&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%2F80kwum6x6klppqvnhstp.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%2F80kwum6x6klppqvnhstp.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These requirements include things like sufficient memory, disk space, and, importantly, a Windows build of 1900 or higher. You can check your Windows build by running the &lt;code&gt;dxdiag&lt;/code&gt; command, which will show you all the juicy details about your system. &lt;/p&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%2Ffchf3r2uw5ll1xosa13n.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%2Ffchf3r2uw5ll1xosa13n.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣Virtualization – It’s a Must!
&lt;/h3&gt;

&lt;p&gt;Next, I made sure that virtualization was enabled on my machine. Docker relies on virtualization to create its containers, so this step is crucial. You can check whether it’s enabled by opening the &lt;strong&gt;Task Manager&lt;/strong&gt; and looking for the virtualization status under the "Performance" tab. &lt;/p&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%2F398qyx7s4pqknrtfnsbr.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%2F398qyx7s4pqknrtfnsbr.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it’s enabled, you’re good to go! If not… well, it’s time to take a trip to your BIOS settings and turn it on,&lt;a href="https://docs.docker.com/desktop/troubleshoot/topics/#virtualization" rel="noopener noreferrer"&gt; check this out&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣Enable Windows Features
&lt;/h3&gt;

&lt;p&gt;Before installing Docker, there are a couple of important Windows features that need to be activated: Windows Subsystem for Linux and Virtual Machine Platform. These are essential for Docker to run smoothly.&lt;/p&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%2Fvszv0ap0er0ihow0co3p.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%2Fvszv0ap0er0ihow0co3p.png" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s how to activate them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the run system and type 'windows featuere'&lt;/li&gt;
&lt;li&gt;Scroll through the list and check the boxes for:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Windows Subsystem for Linux&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Virtual Machine Platform&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Click OK and let Windows do its thing. You’ll probably need to restart your computer to apply these changes.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Once these features are enabled, you’re ready to proceed with the Docker installation. 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  4️⃣Download and Install Docker
&lt;/h3&gt;

&lt;p&gt;Now that my system was fully prepped, I went ahead and downloaded the Docker installer from the official website. The installation process was smooth—just a few clicks, and Docker was up and running on my PC. 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  5️⃣Fire Up Docker
&lt;/h3&gt;

&lt;p&gt;With Docker successfully installed, I launched Docker Desktop, and just like that, my system was ready to start spinning up containers like a pro. 🚢&lt;/p&gt;

&lt;p&gt;And there we have it—mission two complete! Docker is now installed, next I’ll walk you through setting up your first Docker container and running your Next.js project inside it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final Mission : Start Docker
&lt;/h2&gt;

&lt;p&gt;With Docker installed and ready to roll, it was time for the final mission: testing the installation. I was about to take my first dive into containerized waters, and luckily, Docker provided a handy little lifeboat—a sample project called &lt;code&gt;docker/welcome-to-docker&lt;/code&gt;. 🛳️&lt;/p&gt;

&lt;p&gt;Step 1: Launch Docker Desktop&lt;br&gt;
First things first, I launched Docker Desktop from the Start menu. You’ll notice Docker starts running in the background, quietly preparing to do its container magic.&lt;/p&gt;

&lt;p&gt;Step 2: Accessing the CLI&lt;br&gt;
Now, it was time to get my hands dirty with some command-line interface (CLI) action. Since Docker works best with Linux distributions, I needed to make sure I was operating in the right environment. In my case, that meant switching to Ubuntu (remember we already exit just now☺️).&lt;/p&gt;

&lt;p&gt;To switch to Ubuntu, I opened my CLI and typed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This moved me into my Ubuntu environment, where Docker commands are at home. 🐧💻&lt;/p&gt;

&lt;p&gt;Step 3: Running the Docker Welcome Project&lt;br&gt;
With the environment set, it was time to spin up my first Docker container using Docker’s welcome project. Here’s the command I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 80:80 docker/welcome-to-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: The -d flag runs the container in detached mode, meaning it runs in the background, and the -p 80:80 part maps the container’s port 80 to my machine’s port 80. Translation: the container is now accessible via my web browser.)&lt;/em&gt;&lt;/p&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%2Flo5fx3mlk9os0z10dk5k.gif" 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%2Flo5fx3mlk9os0z10dk5k.gif" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;br&gt;
After running this command, Docker fired up the welcome container in the background, and I could visit &lt;a href="http://localhost" rel="noopener noreferrer"&gt;http://localhost&lt;/a&gt; in my browser to see the "Welcome to Docker" message. Success! 🎉&lt;/p&gt;

&lt;p&gt;Step 4: Stopping the Container&lt;br&gt;
Once I’d taken in all the glory of my first running container, it was time to shut it down. To do this, I needed the container’s ID. I found it by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ps -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This listed all running containers, and from there, I grabbed the container ID. With the ID in hand, I issued the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker stop [container_id]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And just like that, my container was stopped, quietly resting until needed again. 🛌
&lt;/h2&gt;

&lt;p&gt;So there you have it—Docker is now installed on Windows. If you found this blog post helpful, feel free to share it with others who might benefit from it. And hey, why not hit that follow button for more nerdy goodness on JavaScript, React, and all things web development?&lt;/p&gt;

&lt;p&gt;Let's stay in touch on &lt;a href="https://www.instagram.com/brokariim/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://x.com/BroKariim" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/BroKarim" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—where the real magic happens.&lt;/p&gt;

&lt;p&gt;Thanks for sticking around! 😊&lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install" rel="noopener noreferrer"&gt;Install linux in windows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/desktop/install/windows-install/" rel="noopener noreferrer"&gt;Docker in windows by docker doc&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Effortless Payments with Lemon Squeezy | Next.js Integration Made Simple</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Sun, 25 Aug 2024 08:08:07 +0000</pubDate>
      <link>https://dev.to/brokarim/effortless-payments-with-lemon-squeezy-nextjs-integration-made-simple-1k1o</link>
      <guid>https://dev.to/brokarim/effortless-payments-with-lemon-squeezy-nextjs-integration-made-simple-1k1o</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;For many entrepreneurs, the payment process feels like the ultimate test of patience. Just when you think you've finally untangled it all, another layer of complications pops up, reminding you that smooth sailing is still a distant dream.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcDNxMzVhejhiMnkwMXk1YTNrODk0NnA3emU5NHE3NGUycWJ0cmQ4NSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Md4xQfuJeTtx6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcDNxMzVhejhiMnkwMXk1YTNrODk0NnA3emU5NHE3NGUycWJ0cmQ4NSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Md4xQfuJeTtx6/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You feel the same? Lemon Squeezy is your &lt;strong&gt;aspirin&lt;/strong&gt;!&lt;br&gt;
This magical payment potion simplifies everything, so you can ditch the payment drama and focus on the fun stuff. No more coding contortions needed. It's like having a payment unicorn on your team.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why LemonSqueezy?
&lt;/h1&gt;

&lt;p&gt;Well, imagine running your SaaS business without needing a PhD in tax compliance or an endless supply of aspirin for payment headaches. LemonSqueezy streamlines it all, from payments and subscriptions to global tax compliance and fraud prevention. &lt;/p&gt;

&lt;p&gt;Plus, it’s got your back with multi-currency support and a storefront ready for all kinds of digital products. It’s like having a tech-savvy business partner who handles all the boring stuff so you can focus on what you do best—creating! Perfect for digital creators, entrepreneurs, and anyone who prefers clicking buttons to coding solutions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Project Setup
&lt;/h1&gt;

&lt;p&gt;Before we dive in, I just want to say that you can find the full code in my &lt;a href="https://github.com/BroKarim-Project/nextjs-lemonsqueezy" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; and catch the demo on my Instagram. Now, about this project on GitHub—it’s got two payment options: first, the classic one-time payment; second, the ever-fancy subscription model.&lt;/p&gt;

&lt;p&gt;But for this tutorial, we’re going all-in on once time payment. Oh, and for my example, I’m using a monthly house cleaning service as the case study. It might sound a tad absurd, but hey, it’s all part of our coding workout! 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setup LemonSqueezy
&lt;/h2&gt;

&lt;p&gt;In order to get started you should have created a store in Lemon Squeezy as well as some products and variants. &lt;/p&gt;

&lt;p&gt;Make sure you have test mode ON. On publishing the store, it will turn OFF; check on the bottom left side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fzcceu8y5ntegzkht9f95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fzcceu8y5ntegzkht9f95.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how my product look like &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxkwksapr96woarrx9rz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxkwksapr96woarrx9rz7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's generate an API Key at &lt;a href="https://app.lemonsqueezy.com/settings/api" rel="noopener noreferrer"&gt;https://app.lemonsqueezy.com/settings/api&lt;/a&gt; to connect to our store:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F1eg6rud0dgxxgpo62far.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F1eg6rud0dgxxgpo62far.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add this as an environment variable to your Next.js project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

LEMONSQUEEZY_API_KEY="[YOUR API KEY]"


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Setuup the route handler
&lt;/h2&gt;

&lt;p&gt;Next, create an API route to handle the payment procces, In this part, the final result we want is to obtain a &lt;code&gt;checkoutUrl&lt;/code&gt;  which we will later pass to the Frontend section.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

export const dynamic = "force-dynamic";

export async function POST(req: NextRequest) {
  try {

    const reqData = await req.json();

    if (!reqData.productId) {
      console.error("Product ID is missing");
      return NextResponse.json({ message: "Product ID is required" }, { status: 400 });
    }


    const response = await lemonSqueezyApiInstance.post("/checkouts", {
      data: {
        type: "checkouts",
        attributes: {
          checkout_data: {
            custom: {
              user_id: "123",
            },
          },
        },
        relationships: {
          store: {
            data: {
              type: "stores",
              id: process.env.LEMON_SQUEEZY_STORE_ID?.toString(),
            },
          },
          variant: {
            data: {
              type: "variants",
              id: reqData.productId.toString(),
            },
          },
        },
      },
    });

    const checkoutUrl = response.data.data.attributes.url;
    console.log(response.data);
    return NextResponse.json({ checkoutUrl });
  } catch (error) {
    console.error("Error in POST /api/lemonsqueezy:", error);
    return NextResponse.json({ message: "An error occured" }, { status: 500 });
  }
}



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

&lt;/div&gt;

&lt;p&gt;Here's a simple explanation for this code : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;firs we ensures that the page is always dynamically rendered, which is important for real-time data by using &lt;code&gt;export const dynamic = "force-dynamic";&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Define async function that handles POST requests to this API route, The function first checks if a product ID is provided. If not, it returns an error message.&lt;/li&gt;
&lt;li&gt;Next we do Api Call to lemonsqueezy to creates a new checkout session , including details like the store ID and product variant.&lt;/li&gt;
&lt;li&gt;To get &lt;code&gt;storeId&lt;/code&gt;, Go to settings for that &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fslxsblt3obm79cw2ms6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fslxsblt3obm79cw2ms6j.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After the Api call , it extracts the checkout URL from the response: &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;const checkoutUrl = response.data.data.attributes.url;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, it returns this URL in the response:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;return NextResponse.json({ checkoutUrl });&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To make sure our API is working correctly, we need to test it. I use a tool called Postman for this. Before we start,we need the &lt;code&gt;variantId&lt;/code&gt; of our product You can find this in your &lt;strong&gt;LemonSqueezy dashboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdiakuqp06mx86shfp558.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdiakuqp06mx86shfp558.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything is working correctly, you should get a response that includes a &lt;code&gt;checkoutUrl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqu7xo2pa48stya5mmp4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqu7xo2pa48stya5mmp4c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. CreatIng the UI &amp;amp; Call the item data
&lt;/h2&gt;

&lt;p&gt;Now that we've laid the groundwork, our next step is time to make the frontend look good, I am a huge fan of &lt;strong&gt;TailwindCSS&lt;/strong&gt; so i make the pricing card with them&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fwym6y59b25p9pd9aj3dv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fwym6y59b25p9pd9aj3dv.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
the code is availale &lt;a href="https://github.com/BroKarim-Project/nextjs-lemonsqueezy" rel="noopener noreferrer"&gt;here&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Next lets set up an &lt;code&gt;async&lt;/code&gt; function that calls the API route we just created. The function will send a POST request with the &lt;code&gt;productId&lt;/code&gt; and, in return, get the checkout URL. Once you have the URL, open it in a new tab to send the user to the payment page.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 const buyProcut1 = async () =&amp;gt; {
    try {
      const response = await axios.post("../api/lemonsqueezy", {
        productId: "495244",
      });

      console.log(response.data);
      window.open(response.data.checkoutUrl, "_blank");
    } catch (error) {
      console.error(error);
      alert("Failed to buy product #1");
    }
  };



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

&lt;/div&gt;

&lt;p&gt;That code is about &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines an asynchronous function called &lt;code&gt;buyProduct1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next send a request to your server with a specific &lt;code&gt;productId&lt;/code&gt;, If success opens a new browser tab with the checkout URL &lt;/li&gt;
&lt;li&gt;If anything goes wrong during the process, it catches the problem, logs it, and shows an alert to the user saying the purchase failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Setup Webhook
&lt;/h2&gt;

&lt;p&gt;Last but not least, we're setting up webhooks to keep track of orders. Head back to your LemonSqueezy dashboard and set up a webhook. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7a4sv4pxhyr5y6ryuxcv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7a4sv4pxhyr5y6ryuxcv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the URL, you’ll need something publicly accessible, which is tricky during local development. This is where &lt;strong&gt;ngrok&lt;/strong&gt; comes in handy.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ngrok&lt;/code&gt; will give you a temporary public URL that forwards to your local machine, You can check this link to setup ngrok in your device : &lt;br&gt;
&lt;a href="https://dashboard.ngrok.com/get-started/setup/" rel="noopener noreferrer"&gt;https://dashboard.ngrok.com/get-started/setup/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like before, the &lt;a href="https://github.com/BroKarim-Project/nextjs-lemonsqueezy" rel="noopener noreferrer"&gt;code&lt;/a&gt; to handle the webhook is already done for you. All you need to do is set it up in your route handler and enjoy the sweet&lt;/p&gt;




&lt;p&gt;Let's stay in touch on &lt;a href="https://www.instagram.com/brokariim/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://x.com/BroKariim" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/BroKarim" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—where the real magic happens.&lt;/p&gt;

&lt;p&gt;Thanks for sticking around! 😊&lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>payment</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Newsletter with Next.js | Nextjs Mailchimp API Integration✉️</title>
      <dc:creator>Bro Karim</dc:creator>
      <pubDate>Sun, 18 Aug 2024 10:03:53 +0000</pubDate>
      <link>https://dev.to/brokarim/newsletter-with-nextjs-nextjs-mailchimp-api-integration-536a</link>
      <guid>https://dev.to/brokarim/newsletter-with-nextjs-nextjs-mailchimp-api-integration-536a</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Do you feel like your email inbox is too empty and needs a little useful 'spam'?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExOGY2eGJjeHZueHF0bnE0MWpmbGRubTBneHRnNGhhY3RnYXlndWs3ZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l0IylR4JqKHLjaP60/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExOGY2eGJjeHZueHF0bnE0MWpmbGRubTBneHRnNGhhY3RnYXlndWs3ZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l0IylR4JqKHLjaP60/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No worries—let’s fix it with some good old-fashioned email magic! Today, we’ll dive into setting up a Mailchimp newsletter subscription in your Next.js app&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Mailchimp
&lt;/h1&gt;

&lt;p&gt;Well, let's dive into the world of email marketing without emptying our wallets, shall we? Mailchimp's &lt;strong&gt;free&lt;/strong&gt; tier perfect for us newsletter newbies who aren't ready to break the bank. &lt;br&gt;
But wait....&lt;/p&gt;

&lt;p&gt;there's more! Mailchimp isn't just a pretty face with a cute monkey logo. It's got a &lt;strong&gt;flexible API&lt;/strong&gt; that's letting you bend and twist your newsletter form to your heart's content. And let's not forget its &lt;strong&gt;popularity&lt;/strong&gt; among developers , which means when you're stuck, you're never alone. So, whether you're a penny-pinching startup or a customization enthusiast, Mailchimp's got your back. &lt;/p&gt;

&lt;h1&gt;
  
  
  Project Setup
&lt;/h1&gt;

&lt;p&gt;Before we begin,I just want to say that you can get the full code in my GitHub &lt;a href="https://github.com/BroKarim/luncurkan" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setting Up Mailchimp
&lt;/h2&gt;

&lt;p&gt;To begin, you'll need to &lt;a href="https://login.mailchimp.com/signup/" rel="noopener noreferrer"&gt;set up a Mailchimp account&lt;/a&gt; and obtain the necessary credentials&lt;/p&gt;

&lt;p&gt;After creating your account, it's time to grab your &lt;a href="https://mailchimp.com/help/about-api-keys/#find+or+generate+your+api+key" rel="noopener noreferrer"&gt;API keys&lt;/a&gt;. If you're a seasoned pro with API keys, you can jump straight to the keys section. For the rest of us mere mortals, there's a handy guide to walk you through it.&lt;/p&gt;

&lt;p&gt;Once you're on the API keys page, look for the "Create A Key" button. When naming your key, get creative but practical. Think of it as naming a pet rock – make it memorable and relevant, like "blog-newsletter-signup". This way, you won't scratch your head later wondering what on earth that key was for. Remember, a well-named API key is a happy API key!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdx9llh8sazsesh3y3n5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdx9llh8sazsesh3y3n5y.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once, you’ve created a key, copy it and put it somewhere safe so you can use it later.&lt;/p&gt;

&lt;p&gt;Next, you need a way to add a user to your audience when they hit the &lt;code&gt;subscribe&lt;/code&gt; button on your form.&lt;/p&gt;

&lt;p&gt;With your account set up, your API key, Audience ID, and API server region you are ready to get into coding up your newsletter. Let’s start by creating some environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Configuring Environment Variables for Mailchimp API
&lt;/h2&gt;

&lt;p&gt;To keep your audience id and API keys secure, it's recommended to create environment variables. Otherwise, someone could see your keys and Audience ID in the request data.&lt;/p&gt;

&lt;p&gt;To create environmental variables you need to create a &lt;code&gt;.env&lt;/code&gt; file in the root directory of your project. This lets you access these values without hardcoding them to your form.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MAILCHIMP_API_KEY=xxxxxx-us20
MAILCHIMP_API_SERVER=us20
MAILCHIMP_AUDIENCE_ID=xxxxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Remember to add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file to keep your credentials secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Creating API Route for Newsletter Subscription
&lt;/h2&gt;

&lt;p&gt;Next, create an API route to handle the subscription process. This route will communicate with the Mailchimp API to add new subscribers. Create a file named &lt;code&gt;app/api/subscribe/route.ts&lt;/code&gt;, Here how my file look like : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fnwnc4mb8r0tbl68bvi5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnwnc4mb8r0tbl68bvi5u.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now lets make a request to Mailchimp. You can achieve this by using Nextjs API routes with the following code : &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { NextApiResponse } from 'next';
import { NextResponse } from 'next/server';

export async function POST(req: Request, res: NextApiResponse) {
  try {
    const { email } = await req.json();

    if (!email) {
      return res.status(400).json({ error: 'Email is required' });
    }

    const MailchimpKey = process.env.MAILCHIMP_API_KEY;
    const MailchimpServer = process.env.MAILCHIMP_API_SERVER;
    const MailchimpAudience = process.env.MAILCHIMP_LIST_ID;

    if (!MailchimpKey || !MailchimpServer || !MailchimpAudience) {
      throw new Error('Missing Mailchimp environment variables');
    }

    const customUrl = `https://${MailchimpServer}.api.mailchimp.com/3.0/lists/${MailchimpAudience}/members`;

    const response = await fetch(customUrl, {
      method: 'POST',
      headers: {
        Authorization: `Basic ${Buffer.from(`anystring:${MailchimpKey}`).toString('base64')}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email_address: email,
        status: 'subscribed',
      }),
    });

    if (!response.ok) {
      const errorData = await response.json();
      return res.status(response.status).json({ error: errorData.detail });
    }

    const received = await response.json();
    return NextResponse.json(received);
  } catch (error) {
    console.error('Error:', error);
    return res.status(500).json({ error: 'Internal Server Error' });
  }
}

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

&lt;/div&gt;

&lt;p&gt;This API route receives the email address, validates it, and sends a request to Mailchimp to add the subscriber.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Creating a Newsletter Subscribe form
&lt;/h2&gt;

&lt;p&gt;Finally, let's create a user-friendly subscription form using Formik for form handling and Yup for validation. Here's an example of how to implement the form:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import React, { useState, useRef, useEffect } from "react";
import * as Yup from "yup";
import { Formik, Form, Field, ErrorMessage } from "formik";

type Props = {};

const requiredSchema = Yup.object({
  email: Yup.string().email("Invalid email").required("Email is required"),
});

function SubscribeForm({}: Props) {
  const [status, setStatus] = useState&amp;lt;number | null&amp;gt;(null);
  const [message, setMessage] = useState&amp;lt;string&amp;gt;("");
  const [buttonDisabled, setButtonDisabled] = useState&amp;lt;boolean&amp;gt;(false);
  const [submitting, setSubmitting] = useState&amp;lt;boolean&amp;gt;(false);
  const [run, setRun] = useState&amp;lt;boolean&amp;gt;(false);
  const [totalCounts, setTotalCounts] = useState&amp;lt;number&amp;gt;(400);
  const [dimensions, setDimensions] = useState({
    width: 0,
    height: 0,
  });

  useEffect(() =&amp;gt; {
    const { innerWidth: width, innerHeight: height } = window;
    setDimensions({
      width,
      height,
    });
  }, []);
  return (
    &amp;lt;div className="flex flex-col space-y-8 md:w-[500px]"&amp;gt;
      &amp;lt;Formik
        initialValues={{
          email: "",
        }}
        validationSchema={requiredSchema}
        onSubmit={async (values, { resetForm }) =&amp;gt; {
          setButtonDisabled(true);
          try {
            const response = await fetch("/api/subscribe", {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({
                email: values?.email,
              }),
            });
            const datas = await response.json();
            if (datas.status &amp;gt;= 400) {
              setStatus(datas.status);
              setMessage("Error joining the newsletter. You can directly contact me at github@ebraj.");
              setTimeout(() =&amp;gt; {
                setMessage("");
                setButtonDisabled(false);
              }, 2000);
              return;
            }

            setStatus(201);
            setMessage("Thank you for subscribing my newsletter 👻.");
            setRun(true);
            setTimeout(() =&amp;gt; {
              setTotalCounts(0);
              setMessage("");
              resetForm();
              setButtonDisabled(false);
            }, 4000);
            setTotalCounts(400);
          } catch (error) {
            setStatus(500);
            setMessage("Error joining the newsletter. You can directly contact me at github@ebraj.");
            setTimeout(() =&amp;gt; {
              setMessage("");
              setButtonDisabled(false);
            }, 2000);
          }
        }}
      &amp;gt;
        &amp;lt;Form className="w-full"&amp;gt;
          &amp;lt;div className="w-full bg-transparent border flex-1 border-black rounded-full flex gap-2 px-3"&amp;gt;
            &amp;lt;Field type="email" name="email" className="w-full grow rounded-md bg-transparent px-5 py-3 outline-none" placeholder="Enter your email" autoComplete="off" /&amp;gt;
            &amp;lt;button className="rounded-full bg-black  my-2 px-4 py-2 font-bold text-gray-100 transition-all hover:scale-105 hover:bg-gray-900 disabled:opacity-80" type="submit" disabled={buttonDisabled}&amp;gt;
              {submitting ? "Submitting" : "Subscribe"}
            &amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
          {message &amp;amp;&amp;amp; &amp;lt;p className={`${status !== 201 ? "text-red-500" : "text-green-500"} pt-4 font-black`}&amp;gt;{message}&amp;lt;/p&amp;gt;}
        &amp;lt;/Form&amp;gt;
      &amp;lt;/Formik&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default SubscribeForm;

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

&lt;/div&gt;

&lt;p&gt;Awesome! Our newsletter form is up and running like a charm!&lt;/p&gt;

&lt;p&gt;So there you have it—Mailchimp is now happily mingling with your Next.js app. If you found this blog post useful, feel free to share the love with others who might need it. And hey, why not hit that follow button for more nerdy goodness on JavaScript, React, and all things web development?&lt;/p&gt;

&lt;p&gt;Let's stay in touch on &lt;a href="https://www.instagram.com/brokariim/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;, &lt;a href="https://x.com/BroKariim" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/BroKarim" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—where the real magic happens.&lt;/p&gt;

&lt;p&gt;Thanks for sticking around! 😊&lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExanFqOW5tb2VwanRhYXF1aTJvdTJrOW8wZHFzemwxanphM25vNGh1dyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ld4dihpAw6I6mpgO5R/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
