<?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: Edgardo Mota</title>
    <description>The latest articles on DEV Community by Edgardo Mota (@edgardo_mota).</description>
    <link>https://dev.to/edgardo_mota</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%2F1924689%2Fce6d8c01-99c8-464a-9590-8e2539e2780a.png</url>
      <title>DEV Community: Edgardo Mota</title>
      <link>https://dev.to/edgardo_mota</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edgardo_mota"/>
    <language>en</language>
    <item>
      <title>Solving a Hydration Error in Next.js with next-themes</title>
      <dc:creator>Edgardo Mota</dc:creator>
      <pubDate>Sun, 24 Aug 2025 14:36:16 +0000</pubDate>
      <link>https://dev.to/edgardo_mota/solving-a-hydration-error-in-nextjs-with-next-themes-bce</link>
      <guid>https://dev.to/edgardo_mota/solving-a-hydration-error-in-nextjs-with-next-themes-bce</guid>
      <description>&lt;p&gt;When developing applications with Next.js, implementing a theme system (light/dark mode) is a common requirement. The &lt;code&gt;next-themes&lt;/code&gt; library is the standard solution for this task, but its interaction with Server-Side Rendering (SSR) can introduce hydration errors if not managed correctly.&lt;/p&gt;

&lt;p&gt;This post documents a specific hydration error encountered while developing a theme-switching component and presents the applied solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context and Initial Code
&lt;/h3&gt;

&lt;p&gt;The goal was to create a simple &lt;code&gt;ThemeToggle&lt;/code&gt; button that would switch between light and dark themes, displaying a sun or moon icon based on the current state.&lt;/p&gt;

&lt;h4&gt;
  
  
  The component was initially implemented as follows:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/ui/ThemeToggle.tsx (Initial version with error)
"use client";

import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";

const ThemeToggle = () =&amp;gt; {
  const { setTheme, resolvedTheme } = useTheme();

  const toggleTheme = () =&amp;gt; {
    const newTheme = resolvedTheme === "dark" ? "light" : "dark";
    setTheme(newTheme);
  };

  return (
    &amp;lt;Button variant="ghost" size="icon" onClick={toggleTheme}&amp;gt;
      {resolvedTheme === "dark" ? (
        &amp;lt;Sun className="h-[1.2rem] w-[1.2rem]" /&amp;gt;
      ) : (
        &amp;lt;Moon className="h-[1.2rem] w-[1.2rem]" /&amp;gt;
      )}
      &amp;lt;span className="sr-only"&amp;gt;Toggle theme&amp;lt;/span&amp;gt;
    &amp;lt;/Button&amp;gt;
  );
};

export { ThemeToggle };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem: Conditional Hydration Error
&lt;/h3&gt;

&lt;p&gt;This component functioned correctly during client-side navigation. However, upon reloading the page &lt;code&gt;F5&lt;/code&gt; with the dark theme enabled, a hydration error occurred: &lt;code&gt;Hydration failed because the server rendered HTML didn't match the client.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The error did not occur when reloading the page in the light theme (the system's default).&lt;/p&gt;

&lt;h3&gt;
  
  
  Root Cause Analysis
&lt;/h3&gt;

&lt;p&gt;The mismatch arises from the state disparity between the server and the client.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Server-Side Render (SSR):&lt;/strong&gt; The server has no access to the browser's &lt;code&gt;localStorage&lt;/code&gt;, where &lt;code&gt;next-themes&lt;/code&gt; persists the user's preference. Therefore, the server renders the page using the default theme (in this case, "system", which resolved to "light"). As a result, the HTML sent to the browser contained the &lt;code&gt;&amp;lt;Moon /&amp;gt;&lt;/code&gt; icon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Side Render:&lt;/strong&gt; Upon page load, the &lt;code&gt;next-themes&lt;/code&gt; script executes, reads the "dark" value from &lt;code&gt;localStorage&lt;/code&gt;, and updates the theme state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Hydration Process:&lt;/strong&gt; React then attempts to "hydrate" the server-rendered HTML. During this process, the &lt;code&gt;ThemeToggle&lt;/code&gt; component's code runs on the client. &lt;code&gt;The useTheme&lt;/code&gt; hook now correctly reports that &lt;code&gt;resolvedTheme&lt;/code&gt; is "dark", so the component logic determines that the &lt;code&gt;&amp;lt;Sun /&amp;gt;&lt;/code&gt; icon should be rendered. At this point, React detects an inconsistency: the server rendered &lt;code&gt;&amp;lt;Moon /&amp;gt;&lt;/code&gt;, but the client expected to render &lt;code&gt;&amp;lt;Sun /&amp;gt;&lt;/code&gt;. This triggers the hydration failure.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Solution: Deferring Client-Dependent Rendering
&lt;/h3&gt;

&lt;p&gt;To resolve this mismatch, we must ensure that the initial client-side render matches what the server sent. The strategy is to delay rendering the theme-dependent UI until the component has successfully mounted on the client.&lt;/p&gt;

&lt;p&gt;This is achieved using the &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt; hooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/ui/ThemeToggle.tsx (Corrected version)
"use client";

import { useState, useEffect } from "react";
import { Moon, Sun, Loader2 } from "lucide-react";
import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";

const ThemeToggle = () =&amp;gt; {
  const { setTheme, resolvedTheme } = useTheme();
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() =&amp;gt; {
    setHasMounted(true);
  }, []);

  const toggleTheme = () =&amp;gt; {
    const newTheme = resolvedTheme === "dark" ? "light" : "dark";
    setTheme(newTheme);
  };

  if (!hasMounted) {
    return (
      &amp;lt;Button variant="ghost" size="icon" disabled&amp;gt;
        &amp;lt;Loader2 className="h-[1.2rem] w-[1.2rem] animate-spin" /&amp;gt;
        &amp;lt;span className="sr-only"&amp;gt;Loading...&amp;lt;/span&amp;gt;
      &amp;lt;/Button&amp;gt;
    );
  }

  return (
    &amp;lt;Button variant="ghost" size="icon" onClick={toggleTheme}&amp;gt;
      {resolvedTheme === "dark" ? (
        &amp;lt;Sun className="h-[1.2rem] w-[1.2rem]" /&amp;gt;
      ) : (
        &amp;lt;Moon className="h-[1.2rem] w-[1.2rem]" /&amp;gt;
      )}
      &amp;lt;span className="sr-only"&amp;gt;Toggle theme&amp;lt;/span&amp;gt;
    &amp;lt;/Button&amp;gt;
  );
};

export { ThemeToggle };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Changes:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;hasMounted&lt;/code&gt; state is introduced, initialized to &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;useEffect&lt;/code&gt; hook with an empty dependency array runs only on the client after the initial mount, setting &lt;code&gt;hasMounted&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;During SSR and the initial client render (before &lt;code&gt;useEffect&lt;/code&gt; runs), the component renders a placeholder loading &lt;code&gt;Loader2&lt;/code&gt;. This output is consistent between the server and the client, preventing the mismatch.&lt;/li&gt;
&lt;li&gt;Once mounted, the component re-renders. Now that &lt;code&gt;hasMounted&lt;/code&gt; is true, it safely displays the correct UI based on the now-reliable &lt;code&gt;resolvedTheme&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt; pattern provides a solution for aligning server and client renders when dealing with state that is only available in the browser. It prevents hydration errors by ensuring the initial HTML is identical and defers the rendering of dynamic UI until the client's state is known and reliable.&lt;/p&gt;

&lt;p&gt;I'm interested to hear about strategies other developers have implemented. What other solutions or custom hooks have you applied to manage hydration errors of this nature in your Next.js projects?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Case Study: A Complete User Flow in Next.js 15, from useSearchParams to Server Actions</title>
      <dc:creator>Edgardo Mota</dc:creator>
      <pubDate>Sat, 23 Aug 2025 17:44:54 +0000</pubDate>
      <link>https://dev.to/edgardo_mota/case-study-a-complete-user-flow-in-nextjs-15-from-usesearchparams-to-server-actions-4pk1</link>
      <guid>https://dev.to/edgardo_mota/case-study-a-complete-user-flow-in-nextjs-15-from-usesearchparams-to-server-actions-4pk1</guid>
      <description>&lt;p&gt;This article details the implementation of a complete user flow in Next.js 15, from selecting a service on the homepage to requesting a specific package on a details page. It covers the technique of passing state between pages using &lt;code&gt;useSearchParams&lt;/code&gt;, conditional rendering based on the URL, and managing user interaction through a modal that communicates with a Server Action to process the request. The goal is to present a practical case study on architectural decisions, code organization, and the lessons learned along the way.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tech Stack &amp;amp; Key Dependencies
&lt;/h3&gt;

&lt;p&gt;The tool selection was focused on performance, type safety, and developer experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Framework:&lt;/strong&gt; Next.js 15 (&lt;code&gt;@next/15.4.6&lt;/code&gt;) with App Router and Turbopack.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Language:&lt;/strong&gt; TypeScript (&lt;code&gt;@typescript/5&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS v4 (&lt;code&gt;@tailwindcss/4&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UI Components:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Headless Primitives:&lt;/strong&gt; Radix UI (&lt;code&gt;@radix-ui/react-select&lt;/code&gt;, &lt;code&gt;@radix-ui/react-dialog&lt;/code&gt;) to ensure accessibility.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Carousels:&lt;/strong&gt; Embla Carousel (&lt;code&gt;embla-carousel-react&lt;/code&gt;) for its lightweight and performant nature.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Backend &amp;amp; Forms:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Server Logic:&lt;/strong&gt; Server Actions.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Validation:&lt;/strong&gt; Zod (&lt;code&gt;zod/4&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Email Delivery:&lt;/strong&gt; Nodemailer (&lt;code&gt;nodemailer/7&lt;/code&gt;) &amp;amp; Mailchimp Marketing (&lt;code&gt;@mailchimp/mailchimp_marketing&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Testing:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Framework:&lt;/strong&gt; Jest (&lt;code&gt;jest/30&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Environment:&lt;/strong&gt; JSDOM (&lt;code&gt;jest-environment-jsdom&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Libraries:&lt;/strong&gt; Testing Library (&lt;code&gt;@testing-library/react&lt;/code&gt;, &lt;code&gt;@testing-library/user-event&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Project Architecture
&lt;/h3&gt;

&lt;p&gt;Code organization was a priority to ensure scalability and maintainability. A modular structure was adopted to clearly separate concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── actions/ # Server-side logic (Server Actions)
├── app/ # Routes and layouts (App Router)
├── components/ # React Components
│ ├── sections/ # Page sections (composition)
│ └── ui/ # Base UI components (atomic)
├── data/ # Static data and content (single source of truth)
├── lib/ # Utility functions and config
├── schemas/ # Validation schemas with Zod
└── types/ # Global type definitions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Note: Tests (&lt;code&gt;__tests__&lt;/code&gt;) are co-located with the modules they test, facilitating discovery and maintenance.)&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Functional Requirement: A Context-Aware Navigation Flow
&lt;/h3&gt;

&lt;p&gt;The goal was to design a user flow that wasn't generic. A user's selection on the main page had to pre-configure the state of a different details page (&lt;code&gt;/cursos&lt;/code&gt;), displaying the relevant information immediately. In turn, selecting a specific package on this page needed to transfer its context to a request modal, minimizing friction for the user.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step-by-Step Technical Implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How to pass the initial selection context between pages?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;useRouter&lt;/code&gt; hook from &lt;code&gt;next/navigation&lt;/code&gt; was used in the source component (&lt;code&gt;ServicesSection.tsx&lt;/code&gt;) to build a URL with a search parameter (&lt;code&gt;?tab=...&lt;/code&gt;) that encodes the user's choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/sections/home/ServicesSection.tsx
import { useRouter } from 'next/navigation';

// ...

const router = useRouter();

const handleLearnMore = (serviceType: string) =&amp;gt; {
  // Build and navigate to the URL with the selected service as the search parameter
  router.push(`/cursos?tab=${serviceType}`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;How does the destination page react to this context?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The destination component (&lt;code&gt;CoursesSection.tsx&lt;/code&gt;) was implemented as a Client Component that uses the &lt;code&gt;useSearchParams&lt;/code&gt; hook to read the &lt;code&gt;tab&lt;/code&gt; parameter from the URL. This value is used to initialize the &lt;code&gt;activeTab&lt;/code&gt; state, ensuring the correct content is displayed on the first render, even if the page is reloaded or the link is shared.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/sections/cursos/CoursesSection.tsx
import { useSearchParams } from 'next/navigation';
import { useState, useEffect } from 'react';

// ...

// Reads URL parameters from the client side
const searchParams = useSearchParams();
const tabFromUrl = searchParams.get('tab');

// A helper function to validate the parameter and return a safe value
const getValidatedTab = (tab: string | null): TabKey =&amp;gt; {
  const validTabs = Object.keys(coursesData);
  if (tab &amp;amp;&amp;amp; validTabs.includes(tab)) {
    return tab as TabKey;
  }
  return 'personalizado'; // Returns a default value if the parameter is invalid or null
};

// Initialize the component state directly from the URL
const [activeTab, setActiveTab] = useState&amp;lt;TabKey&amp;gt;(() =&amp;gt; getValidatedTab(tabFromUrl));

// (Optional) Sync state if user navigates back/forward
useEffect(() =&amp;gt; {
    setActiveTab(getValidatedTab(searchParams.get('tab')));
}, [searchParams]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;How to handle the final request securely and efficiently?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Selecting a package triggers a &lt;code&gt;ServiceRequestModal&lt;/code&gt;. This component encapsulates the final conversion logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; It receives the &lt;code&gt;serviceName&lt;/code&gt; as a prop to personalize its content.&lt;/li&gt;
&lt;li&gt; It presents a minimal form (only email) to reduce friction.&lt;/li&gt;
&lt;li&gt; It uses &lt;code&gt;useActionState&lt;/code&gt; to call the &lt;code&gt;submitServiceRequest&lt;/code&gt; Server Action.&lt;/li&gt;
&lt;li&gt; The loading state (&lt;code&gt;pending&lt;/code&gt;) is automatically managed via a &lt;code&gt;SubmitButton&lt;/code&gt; sub-component that uses &lt;code&gt;useFormStatus&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/components/sections/cursos/ServiceRequestModal.tsx
'use client';

import React, { useEffect } from 'react';
import { useActionState, useFormStatus } from 'react';
import { Modal } from '@/components/ui/Modal';
import { Input } from '@/components/ui/Input';
import { Button } from '@/components/ui/Button';
import { submitServiceRequest } from '@/actions/serviceRequest';

// Subcomponent to access the 'pending' state
function SubmitButton() {
    const { pending } = useFormStatus();
    return (
        &amp;lt;Button type="submit" loading={pending} disabled={pending} fullWidth&amp;gt;
            {pending ? 'Enviando...' : 'Confirmar Solucitud'}
        &amp;lt;/Button&amp;gt;
    );
}

export const ServiceRequestModal = ({ isOpen, onClose, serviceName }) =&amp;gt; {
    // Link the form to the Server Action
    const [state, formAction] = useActionState(submitServiceRequest, { message: '', success: false });

    // Closes the modal automatically if the action was successful
    useEffect(() =&amp;gt; {
        if (state?.success) {
            const timer = setTimeout(() =&amp;gt; onClose(), 2000);
            return () =&amp;gt; clearTimeout(timer);
        }
    }, [state, onClose]);

    return (
        &amp;lt;Modal isOpen={isOpen} onClose={onClose}&amp;gt;
            {state?.success ? (
                &amp;lt;p className="text-green-600"&amp;gt;{state.message}&amp;lt;/p&amp;gt;
            ) : (
                &amp;lt;&amp;gt;
                    &amp;lt;p&amp;gt;
                        Estás solicitando el paquete: &amp;lt;strong&amp;gt;{serviceName}&amp;lt;/strong&amp;gt;
                    &amp;lt;/p&amp;gt;
                    &amp;lt;form action={formAction} className="space-y-4"&amp;gt;
                        &amp;lt;Input
                            name="email"
                            type="email"
                            placeholder="tu@correo.com"
                            required
                        /&amp;gt;
                        {/* Pass the service name to the Server Action in a hidden way */}
                        &amp;lt;input type="hidden" name="serviceName" value={serviceName} /&amp;gt;
                        &amp;lt;SubmitButton /&amp;gt;
                        {state?.message &amp;amp;&amp;amp; &amp;lt;p className="text-sm text-red-600"&amp;gt;{state.message}&amp;lt;/p&amp;gt;}
                    &amp;lt;/form&amp;gt;
                &amp;lt;/&amp;gt;
            )}
        &amp;lt;/Modal&amp;gt;
    );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Alternative Approaches
&lt;/h3&gt;

&lt;p&gt;The implemented solution using &lt;code&gt;useSearchParams&lt;/code&gt; is effective for this use case, as it treats the URL as the source of truth, which is ideal for state that needs to be shareable and persistent. However, it's not the only way to solve this problem. I'm opening the discussion to the community:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context API vs. URL State&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In what scenarios would you have preferred using React's &lt;code&gt;Context API&lt;/code&gt; to manage this state? An advantage could be avoiding URL "pollution," but with the downside of losing state on a page refresh.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Global State Libraries (Redux, Zustand)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a flow like this, would you consider a global state library a viable option? Or would it be overkill for state that isn't truly "global" to the entire application?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Other Techniques?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are there other patterns or tools within the Next.js ecosystem you would have considered for passing state between pages this way? I'd love to read about other implementations.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Resources &amp;amp; Contact
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://silver-dog-training.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/gardogit/silver-dog-training" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source code is available for review. Technical feedback, suggestions for improvement via Issues, or Pull Requests are welcome.&lt;/p&gt;

&lt;p&gt;For technical discussions or professional inquiries, you can find me on &lt;a href="https://www.linkedin.com/in/ruizedgardo/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>usesearchparams</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Axero Frontend Challenge: Building a Dynamic Office Intranet Interface</title>
      <dc:creator>Edgardo Mota</dc:creator>
      <pubDate>Sun, 27 Jul 2025 18:15:50 +0000</pubDate>
      <link>https://dev.to/edgardo_mota/axero-frontend-challenge-building-a-dynamic-office-intranet-interface-40p5</link>
      <guid>https://dev.to/edgardo_mota/axero-frontend-challenge-building-a-dynamic-office-intranet-interface-40p5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend/axero"&gt;Frontend Challenge: Office Edition sponsored by Axero, Holistic Webdev: Office Space&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Corporate Hub: Intelligent Intranet Dashboard
&lt;/h1&gt;

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

&lt;p&gt;We created &lt;strong&gt;Corporate Hub&lt;/strong&gt;, a modern and intelligent corporate intranet dashboard that reimagines how employees interact with their workplace digital environment. Our goal was to build more than just another dashboard – we wanted to create a welcoming, intuitive space that feels like having a personal AI assistant helping you stay productive throughout your workday.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🤖 &lt;strong&gt;Intelligent Welcome Component&lt;/strong&gt;: An AI-like messaging system that provides contextual reminders, follow-ups, and deadlines using natural language&lt;/li&gt;
&lt;li&gt;📰 &lt;strong&gt;Smart News Carousel&lt;/strong&gt;: Mobile-optimized news rotation with responsive design and accessibility features&lt;/li&gt;
&lt;li&gt;📅 &lt;strong&gt;Integrated Event Management&lt;/strong&gt;: Seamless calendar integration with smart notifications&lt;/li&gt;
&lt;li&gt;👥 &lt;strong&gt;Team Discovery&lt;/strong&gt;: New hires showcase with detailed employee profiles&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Application Launcher&lt;/strong&gt;: Quick access to corporate tools and external applications&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;Smart Quick Links&lt;/strong&gt;: Categorized resource organization for efficient navigation&lt;/li&gt;
&lt;li&gt;🌙 &lt;strong&gt;Dark Mode Support&lt;/strong&gt;: Complete theming system with user preference persistence&lt;/li&gt;
&lt;li&gt;♿ &lt;strong&gt;Accessibility First&lt;/strong&gt;: WCAG 2.1 compliant with comprehensive keyboard navigation and screen reader support&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;🔗 &lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://axero-space.netlify.app/" rel="noopener noreferrer"&gt;Corporate Hub Dashboard&lt;/a&gt;&lt;br&gt;&lt;br&gt;
📂 &lt;strong&gt;Source Code&lt;/strong&gt;: &lt;a href="https://github.com/gardogit/OfficeSpace" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Vision
&lt;/h3&gt;

&lt;p&gt;We started with a simple question: "What if your corporate intranet felt as welcoming and intelligent as a personal assistant?" This led us to focus on creating an experience that's both functional and emotionally engaging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Highlights We're Proud Of:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. AI-Inspired Welcome Experience
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Natural language messaging with typing animation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Recuerda revisar la propuesta del cliente que quedó pendiente ayer.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;El equipo de diseño está esperando tu feedback sobre los mockups.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;La documentación técnica vence mañana, ¿ya tienes todo listo?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We implemented a typing effect animation without external libraries, creating messages that feel conversational and human rather than robotic.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Mobile-First Responsive Design
&lt;/h4&gt;

&lt;p&gt;Our news carousel adapts intelligently to different screen sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Desktop: Full navigation with position indicators&lt;/li&gt;
&lt;li&gt;Mobile: Clean interface with edge-to-edge images and simplified controls&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Performance Optimization
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Lazy loading for all dashboard components&lt;/li&gt;
&lt;li&gt;React.memo for preventing unnecessary re-renders&lt;/li&gt;
&lt;li&gt;Code splitting with dynamic imports&lt;/li&gt;
&lt;li&gt;Optimized bundle size with manual chunk splitting&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Accessibility Excellence
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Complete keyboard navigation support&lt;/li&gt;
&lt;li&gt;ARIA labels and live regions for screen readers&lt;/li&gt;
&lt;li&gt;Focus management and skip links&lt;/li&gt;
&lt;li&gt;Color contrast compliance (WCAG 2.1 AA)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Development Process:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Phase 1: Foundation 🏗️
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Set up React + TypeScript + Vite stack&lt;/li&gt;
&lt;li&gt;Implemented design system with Tailwind CSS&lt;/li&gt;
&lt;li&gt;Created reusable component library&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Phase 2: Core Features ⚡
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Built responsive layout system&lt;/li&gt;
&lt;li&gt;Developed news carousel with auto-rotation&lt;/li&gt;
&lt;li&gt;Implemented event management integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Phase 3: Intelligence Layer 🧠
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Created the WelcomeHero component with AI-like messaging&lt;/li&gt;
&lt;li&gt;Added contextual content based on user data&lt;/li&gt;
&lt;li&gt;Implemented smart notifications system&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Phase 4: Polish &amp;amp; Optimization ✨
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Added comprehensive error boundaries&lt;/li&gt;
&lt;li&gt;Implemented loading states and skeleton screens&lt;/li&gt;
&lt;li&gt;Optimized for production deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenges Overcome:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript Compilation Issues:&lt;/strong&gt; Resolved 60+ TypeScript errors for successful Netlify deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile UX Optimization:&lt;/strong&gt; Redesigned components specifically for touch interfaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Bottlenecks:&lt;/strong&gt; Implemented lazy loading and memoization strategies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility Compliance:&lt;/strong&gt; Ensured full keyboard navigation and screen reader support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What We Learned:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User Experience First:&lt;/strong&gt; Technical excellence means nothing without great UX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive Enhancement:&lt;/strong&gt; Start with core functionality, then add intelligence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Matters:&lt;/strong&gt; Every millisecond counts in user perception&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility is Essential:&lt;/strong&gt; Inclusive design benefits everyone&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Team Collaboration:
&lt;/h3&gt;

&lt;p&gt;This project was built in collaboration with &lt;a class="mentioned-user" href="https://dev.to/jonathanbc10"&gt;@jonathanbc10&lt;/a&gt; combining our expertise in frontend development and user experience design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; React 18, TypeScript, Vite&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS with custom design system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Management:&lt;/strong&gt; React Hooks with context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing:&lt;/strong&gt; Vitest with React Testing Library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Netlify with automatic CI/CD&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Lazy loading, code splitting, memoization&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  License
&lt;/h3&gt;

&lt;p&gt;This project is licensed under the MIT License.&lt;/p&gt;

&lt;h4&gt;
  
  
  Open Source Commitment
&lt;/h4&gt;

&lt;p&gt;We believe in the power of open source software. Corporate Hub is freely available for:&lt;/p&gt;

&lt;p&gt;✅ Personal and commercial use&lt;br&gt;
✅ Modification and distribution&lt;br&gt;
✅ Private use&lt;br&gt;
✅ Learning and educational purposes&lt;/p&gt;

&lt;p&gt;Feel free to fork, modify, and build upon our work. We'd love to see what you create!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Corporate Hub&lt;/strong&gt; represents our vision of what modern workplace software should be: intelligent, accessible, and genuinely helpful. We believe that great software should feel less like a tool and more like a thoughtful colleague who's always there to help you succeed.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
