# Latest Articles URL: /blog Blog about web development, design, and other topics. *** title: Latest Articles description: Blog about web development, design, and other topics. date: 2025-06-16T00:00:00.000Z author: name: Mew url: [https://mewis.me](https://mewis.me) ----------------------------------------- import { ArticlesList } from '@/components/articles/articles-list'; # When to use nullish and or operators in JavaScript URL: /blog/code-tips/when-to-use-nullish-and-or-operators-in-javascript A guide to understanding the difference between nullish coalescing and logical OR operators in JavaScript. *** title: When to use nullish and or operators in JavaScript description: A guide to understanding the difference between nullish coalescing and logical OR operators in JavaScript. date: 2025-06-20 parent: "code-tips" tags: \["javascript", "code-tips"] ---------------------------------- ## Introduction In JavaScript, handling missing or falsy values is a common task, especially when working with dynamic data or user inputs. Two operators often used for this purpose are the `??` (nullish coalescing) and `||` (logical OR) operators. While they may appear similar at first glance, they serve slightly different purposes and behave differently in specific scenarios. This article will explain how each operator works and when you should use one over the other. ### Default Value with `??` and `||` The `??` (nullish coalescing) and `||` (logical OR) operators in JavaScript are used to provide a default value when the expression on the left-hand side is not provided. They are helpful when handling cases where a value might not exist or is falsy, and you want to ensure a fallback is provided. ### `??` (Nullish Coalescing) Operator #### What does the `??` operator mean? The `??` operator is used to provide a **default value** when the expression on the left-hand side is either `null` or `undefined`, but allows other falsy values like `0`, `false`, or `''` (empty string) to remain. > ✅ This means even if the value is `0`, `false`, or `''`, it will still be used instead of falling back. ### `||` (Logical OR) Operator #### What does the `||` operator mean? The `||` operator is quite similar to `??`, but it checks for **all falsy values**, not just `null` or `undefined`. This means it will fall back even if the value is `0`, `false`, or an empty string. ## When to Use `??` vs `||` Use `??` when working with APIs, optional parameters, or dynamic data that may not always be defined. This way, you provide a fallback without affecting valid falsy values like `false` or an empty string. Use `||` when you want to treat all falsy values as missing and replace them with a fallback. ## Conclusion Understanding the difference between `??` and `||` is essential for writing reliable and predictable JavaScript code. Use `??` when you only want to check for `null` or `undefined` and allow other falsy values like `0`, `false`, or `''` to pass through. Use `||` when you want to provide a fallback for **any** falsy value. By choosing the right operator for the context, you can avoid unintended bugs and ensure your application behaves as expected. # Optimize Tailwind with ESLint & Prettier URL: /blog/css/boost-your-tailwind-workflow-with-eslint-and-prettier Streamline your Tailwind CSS workflow by integrating ESLint and Prettier to auto-sort class names and enforce best practices *** title: "Optimize Tailwind with ESLint & Prettier" description: "Streamline your Tailwind CSS workflow by integrating ESLint and Prettier to auto-sort class names and enforce best practices" date: 2025-06-19 parent: tailwindcss tags: \['tailwindcss', 'eslint', 'prettier'] -------------------------------------------- ## Introduction [Tailwind CSS](https://tailwindcss.com/) is an essential tool for building modern UIs, but let's be real, managing its class names can get messy fast. Ever found yourself staring at an HTML tag loaded with an endless string of utility classes? Or maybe you've caught yourself wondering, “Did I already add ‘px-4' here?” Keeping class names consistent and ordered isn't just about aesthetics; it's about maintaining sanity in collaborative projects. If you've ever struggled with disorganized Tailwind classes, you're in the right place. In this post, I'll walk you through how to make your Tailwind CSS setup strict and efficient. Using the [Prettier Tailwind CSS plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss), you'll learn how to sort class names following the official Tailwind team's recommendations. Additionally, we'll explore the [ESLint Tailwind CSS plugin](https://www.npmjs.com/package/eslint-plugin-tailwindcss), which not only enforces class order but offers even more capabilities to improve your workflow. By the end, you'll have a clean, consistent codebase that's easier to read, debug, and scale. Let's dive in and clean up that class name chaos. ### Tutorial: Setting Up a Strict Tailwind CSS Workflow #### Step 1: Install Required Dependencies To get started, you'll need to install some packages that will make this setup possible. Run the following command to add them to your project: > **I will be using** [bun](https://bun.sh) as my package manager, but feel free to use whatever works for you. In addition to core ESLint and Prettier packages themselves, this installs both the [Prettier Tailwind CSS plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) for sorting class names and the [ESLint Tailwind CSS plugin](https://www.npmjs.com/package/eslint-plugin-tailwindcss) for validating and enforcing best practices. #### Step 2: Configure Prettier Next, let's set up Prettier to automatically sort Tailwind CSS class names. Open your `.prettierrc` (or [equivalent](https://prettier.io/docs/en/configuration.html)) file (or create one if it doesn't exist) and add the following configuration: Prettier will now automatically sort Tailwind CSS class names every time you format your code. #### Step 3: Configure ESLint ##### ESLint 8 The steps below are for ESLint 8, which is soon to be deprecated, so you might consider upgrading your version. Check our this [migration guide](https://eslint.org/docs/latest/use/configure/migration-guide) for that. Now, let's configure ESLint to enhance your Tailwind CSS setup. Open your `.eslintrc.json` file and update it with these settings: ##### shadcn/ui cn function If you're using utility functions like cn from [shadcn/ui](https://ui.shadcn.com/) [or cva fr](https://ui.shadcn.com/)om the [`class-variance-authority`](https://cva.style/docs) [package, you'll need to](https://cva.style/docs) update your ESLint configuration to recognize these functions. Add the following to your `.eslintrc.json` file: This is particularly useful when you dynamically generate class names, ensuring even complex class strings follow Tailwind's rules. ##### ESLint 9 In ESLint 9, they moved to use a new flat config format (typically configured in an `eslint.config.js` file), and they provide a very helpful [configuration migrator](https://www.npmjs.com/package/@eslint/migrate-config) that we can run on our existing config file to convert it to the new format. To use it do the following to your existing configuration file (`.eslintrc`, `.eslintrc.json`, `.eslintrc.yml`): This will create a starting point for your `eslint.config.js` file but is not guaranteed to work immediately without further modification. If you want to do it manually, you can create a new `eslint.config.js` file and paste the following to it. #### Step 4: Verify the setup With everything configured, it's time to test the setup. Create a file with some unordered Tailwind CSS classes, like this: `} title="index.tsx" lang="xml" /> Run Prettier to format the file, and ESLint to validate it. After formatting, the class names will be ordered automatically: `} title="index.tsx" lang="xml" /> #### Step 5: Add to Your Workflow To make the most of this setup, integrate these tools into your workflow: * Use Prettier with your code editor (e.g., enable auto-formatting in VS Code). * Add ESLint to your CI/CD pipeline or Git hooks using a tool like Husky to enforce rules before committing code. ## Conclusion Keeping class names organized and consistent is crucial for maintaining a clean and readable codebase. With a little effort upfront, you can simplify your workflow, avoid common pitfalls, and create a more collaborative coding environment. Consistency makes all the difference. Thanks for reading this far. # How caching works in Next.js URL: /blog/nextjs/how-caching-works-in-nextjs Learn how caching works in Next.js and how to optimize your app's performance. *** title: How caching works in Next.js description: Learn how caching works in Next.js and how to optimize your app's performance. date: 2025-06-16 parent: nextjs tags: \['nextjs', 'caching'] author: name: Mew url: [https://mewis.me](https://mewis.me) ----------------------------------------- ## Introduction Caching is a powerful technique that significantly enhances the performance and efficiency of web applications. Next.js, a popular React framework, leverages multiple caching strategies to reduce unnecessary data fetching, speed up page loads, and optimize the user experience. By strategically storing data and page content, Next.js minimizes server load and maximizes responsiveness. ### The Four Caching Systems #### 1. Request Memoization This is the most basic form of caching. When your code makes the same data request multiple times during a single page render, Next.js only executes that request once. **For example, if three components on your page need the same data, the actual fetch happens once, and the result is shared.** This happens automatically when you use the fetch API in your components. The cache only lasts for the current request → once the page is sent to the user, this cache is cleared. #### 2. Data Cache The Data Cache stores the results of fetch requests on the server across multiple user requests and even across deployments. When you fetch data in a component, Next.js checks if that data is already in the Data Cache. If it is, it uses the cached data instead of making a new request to your data source. This makes your application faster and reduces load on your databases or APIs. By default, **data is cached indefinitely**, but you can control this by setting revalidation rules (or [**opting out for a page**](https://nextjs.org/docs/app/building-your-application/caching#opting-out-2)): You can also trigger revalidation manually using `revalidatePath` or `revalidateTag` functions. #### 3. Full Route Cache Next.js doesn't just cache data. It can cache entire rendered pages. When a route is statically rendered, Next.js saves: * The HTML of the page * The React Server Component Payload (a compact representation of your server components) This means Next.js doesn't have to re-render these pages for each visitor. Instead, it serves the pre-generated content, making page loads very fast. Routes are statically rendered by default unless you use dynamic features like cookies, headers, or uncached data fetches. #### 4. Router Cache This cache exists in the user's browser memory during a session. When a user navigates between pages, Next.js stores the previously visited routes. This enables instant back/forward navigation without making new requests to the server. This cache is temporary and only lasts during the user's session. It's cleared when the page is refreshed. ### How These Caches Work Together These caching systems don't work alone → they create a complete caching strategy: When building your app, Next.js statically renders pages and stores them in the Full Route Cache. 1. When fetching data, the Data Cache stores results to avoid repeated data fetches. 2. During a single page render, Request Memoization ensures each unique data request only happens once. 3. As users navigate your site, the Router Cache enables fast navigation between pages they've already visited. ### Controlling Cache Behavior You can control caching in several ways: * For data caching, use the `cache` and `next.revalidate` options with `fetch`. * For route caching, use dynamic functions like `cookies()` or `headers()` to make routes dynamic. * For time-based revalidation, use the `revalidate` option. * For on-demand revalidation, use `revalidatePath` or `revalidateTag` in Server Actions. When you want fresh data, you have options: * Revalidate on a time interval * Revalidate on specific events * Opt out of caching entirely ## Conclusion **Request Memoization**: Saves the results of fetch calls during a single page render. It prevents duplicate data fetches within the same component tree. This cache lasts only for the current request and is cleared once rendering completes. It happens automatically with no configuration needed. **Data Cache**: Persists fetch results on the server across multiple user requests and deployments. This allows Next.js to reuse previously fetched data without hitting your data sources repeatedly. This cache is persistent until manually revalidated. Control it using fetch options like `cache: 'no-store'` to disable caching or `next: { revalidate: seconds }` to set time-based revalidation. **Full Route Cache**: Stores pre-rendered HTML and React Server Component Payloads for entire routes. This cache eliminates the need to re-render pages for each visitor. It persists across requests and is invalidated on redeployment or when the Data Cache is revalidated. Routes become dynamic (not cached) when using dynamic functions like cookies, headers, or uncached data fetches. **Router Cache**: Stores previously visited routes in the user's browser memory. This enables instant navigation between pages without additional server requests. This cache lasts for the user's session and is cleared on page refresh. Navigation with the `` component uses this cache automatically. Thank you for reading! # Top 5 Next.js alternatives for React developers URL: /blog/nextjs/top-5-nextjs-alternatives-for-react-developers Discover the top alternatives to Next.js for React developers seeking flexibility and enhanced performance in web app development. *** title: Top 5 Next.js alternatives for React developers description: Discover the top alternatives to Next.js for React developers seeking flexibility and enhanced performance in web app development. date: 2025-06-16 parent: nextjs tags: \['nextjs', 'alternatives'] author: name: Mew url: [https://mewis.me](https://mewis.me) ----------------------------------------- ## Introduction Next.js has earned its place as one of the most popular frameworks in the React ecosystem. It gives you just about everything you need to build a modern web app with React—file-based routing, server-side rendering, static generation, and many other full-stack features. Next.js Over time, Next.js has also become much more complex. Features like the App Router, Server Components, middleware, and streaming APIs have made it powerful but also more opinionated and less predictable. Additionally, Next.js (and Vercel, its parent company) has faced criticism lately, especially around vendor lock-in and the pace of change, leaving developers behind. If you're looking for a Next.js alternative and want to keep using React, you're not short on options. In this article, we'll cover the best frameworks to consider, what they offer, where they shine, and when they might be a better fit than Next.js. ### Next.js alternatives overview Before we proceed with the article, here's a high-level overview of the alternatives we'll be reviewing: | **Framework** | **Best For** | **React Support** | **SSR Support** | **Routing** | **Data Loading** | **Type Safety** | | ----------------------- | ------------------------------------------------------ | ----------------- | --------------------- | ---------------------------------- | ---------------------------------------- | -------------------- | | **Remix** | **Full-stack apps with built-in form/data primitives** | **Full** | **Yes** | **File-based + loaders/actions** | **Loaders + actions (built-in)** | **Yes** | | **Astro** | **Content-heavy static or hybrid sites** | **Partial** | **Yes (newer)** | **File-based with islands** | **JS fetch in Astro components** | **Yes** | | **TanStack Start** | **Fully type-safe full-stack React apps** | **Full** | **Yes** | **File-based via TanStack Router** | **Server functions + typed loaders** | **Yes (full stack)** | | **Vike** | **Full control over SSR/SSG with minimal abstraction** | **Full** | **Yes** | **Convention-based (+Page.tsx)** | **Custom server Hooks (onBeforeRender)** | **Yes** | | **Vite + React Router** | **Lightweight client-side React apps** | **Full** | **No (manual setup)** | **Manual via React Router** | **React Router loaders** | **Yes** | ### Remix Remix [Remix](https://remix.run/) is one of the strongest alternatives to Next.js, especially if you still want to stick with React but don't like the direction Next.js is going. Remix respects how the web works. It doesn't try to abstract everything into framework magic; instead, it builds on native browser features like forms, caching, and HTTP requests and gives you a modern, React-first experience on top of that. It's also completely platform-agnostic. Let's explore some of its core features and drawbacks below. #### Features * **File-based routing –** Like Next.js, Remix uses a file-based routing system. However, instead of just mapping files to pages, each route can define its `loader()` for data fetching and `action()` for mutations like form submissions. These functions run server-side and are tightly scoped to the route itself, with no extra API layer required. * **Built-in SSR –** SSR is the default in Remix, but it's not forced. Data is fetched server-side via loaders, injected directly into components, and hydrated client-side without requiring suspense, RSC, or client fetch hacks. * **Smart caching strategy –** Remix uses standard HTTP caching headers. You control caching behavior using Cache-Control, ETags, etc. #### Drawbacks * **Smaller ecosystem –** Compared to Next.js, Remix has fewer plugins, integrations, and tutorials. You'll find yourself building more from scratch or reading source code * **Opinionated about the web –** Remix does a lot of things the web way, and while this is powerful, it's not for everyone. If you prefer high-level abstractions over explicit control, it might feel like more work #### Developer experience Similar to Next.js, Remix uses a file-based routing system but with a more focused design. Each route is fully self-contained and handles its data fetching with a `loader()` function and mutations with an `action()`. There's no need to fetch data at the top level or pass props down manually. For example, here's what a simple blog route looks like in Remix: { const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5"); if (!res.ok) throw new Response("Failed to fetch posts", { status: res.status }); const posts = await res.json(); return json({ posts }); }; export default function Blog() { const { posts } = useLoaderData(); return (

Latest Posts

    {posts.map((post) => (
  • {post.title}

    {post.body}

  • ))}
); }`} lang="tsx" title={`blog.tsx`} /> This example blog page fetches data on the server using the `loader()` and renders the posts without any client-side fetching or prop drilling. Remix also lets you use standard React patterns if you do want client-side interactivity: { setLoading(true); const res = await fetch(\`https://jsonplaceholder.typicode.com/posts?q=\${query}\`); const data = await res.json(); setResults(data); setLoading(false); }; return (

Search Posts

setQuery(e.target.value)} />
    {results.map((post) => (
  • {post.title}
  • ))}
); }`} lang="tsx" title={`search.tsx`} /> Remix doesn't get in the way of standard React. It just makes server-side logic simpler and more structured when you need it. ### Astro Astro [Astro](https://astro.build/) isn't explicitly a React framework. However, it integrates well with React, and depending on what you're building, it might be a better fit than Next.js. Astro is built for content-heavy sites such as blogs, landing pages, documentation, etc., where performance matters more than full client-side interactivity. Its core strength is the island architecture, which renders everything as static HTML by default, and only the interactive parts of the page are hydrated with JavaScript. This way, you get tiny JS bundles and fast page loads. #### Features * **Partial hydration –** Instead of booting up a full React app on the client, Astro sends down plain HTML and hydrates components only where needed using its islands architecture * **Framework agnostic –** You can use React, Vue, Svelte, Solid, or Preact, even in the same project * **Built-in markdown/MDX support –** First-class content authoring experience. Embed components directly in your .mdx files without extra tooling * **Hybrid rendering –** While Astro started as a static site generator, it now supports SSR and hybrid rendering. You can statically generate most pages and use SSR only where you need it #### Drawbacks * **Not a full React app –** Astro is great for static and semi-dynamic sites, but if you're building a highly interactive SPA or complex dashboard, it might feel limiting * **Less mature SSR –** SSR support exists and works, but it's still newer and not as widely battle-tested as Next.js or Remix in complex, dynamic environments #### Developer experience As mentioned earlier, Astro pages are mostly HTML-first. You can drop in React (or any framework) components wherever interactivity is needed. Here's a basic example of a blog page: Blog

Latest Posts

`} lang="tsx" title={`blog.astro`} /> In this case, `BlogList` is a React component that only gets hydrated on the client when needed, as shown below. The rest of the page is just static HTML: {posts.map((post) => (
  • {post.title}

    {post.body}

  • ))} ); }`} lang="tsx" title={`BlogList.tsx`} /> You can also go full client-side inside your components when it makes sense. Astro stays out of the way and doesn't try to control your whole app. ### TanStack Start TanStack Start [TanStack Start](https://tanstack.com/start/latest) is a new full-stack React framework built by the team behind [TanStack Query](https://tanstack.com/query/latest), [Table](https://tanstack.com/table/latest), and Virtual. It's not trying to reinvent the wheel; instead, it aims to make it easier to build fast, type-safe React apps using rock-solid tools. It supports SSR, streaming, server functions, and full-stack TypeScript without forcing a specific structure. #### Features * **Server-side rendering (SSR) and streaming –** TanStack Start supports both full-document SSR and streaming out of the box, enhancing performance and SEO * **Server functions and API routes –** You can define server functions that run exclusively on the server, facilitating data fetching and other server-side operations #### Drawbacks * **Still early –** The framework is currently in beta, which means some features may change, and the documentation might be incomplete #### Developer experience TanStack Start feels closer to building a pure React app than most frameworks. You're not stuck in someone else's opinionated folder structure, and there's almost zero framework boilerplate. Routing is file-based but fully typed with TanStack Router, so navigation and data fetching are predictable and safe. Here's a basic route with server data fetching: { return { posts: await fetchPosts(), }; }, component: PostsPage, }); function PostsPage({ posts }) { return (

    Blog Posts

      {posts.map(post => (
    • {post.title}
    • ))}
    ); } `} lang="tsx" title={`posts.tsx`} /> And the server function could look like this: With this setup, your logic lives where it makes sense, and everything is fully typed from front to back. It also doesn't force you into black-box APIs. ### Vike Vike [Vike, (formerly vite-ssr)](https://vike.dev/) is a lightweight meta-framework for building React apps with full control over routing, server-side rendering, and static site generation. It's built on top of Vite and designed to be unopinionated, i.e., no forced folder structures, no black-box abstractions, and no hidden behaviors. If you're coming from Next.js and feeling boxed in by conventions, Vike offers a more transparent and flexible alternative. #### Features * **Flexible file-based routing –** Vike uses convention-based files (+Page.tsx, +Page.server.ts, +route.ts), but doesn't impose a strict folder structure. You get to define how your app is organized * **Control over SSR & SSG –** You can choose per route whether a page should be statically generated or rendered on the server. Both options are first-class and fully configurable * **Unopinionated by design –** Vike also lets you decide which tools to use for data fetching, routing, caching, authentication, and layouts. It provides the core primitives and leaves the rest to you #### Drawbacks * **No built-in data layer –** Vike doesn't include a built-in system for data loading like Remix's `loader()`. You're responsible for handling fetch logic, caching, and revalidation yourself. This gives you flexibility but adds setup overhead if you don't already have a pattern in place * **Higher barrier to entry –** Vike assumes familiarity with certain conventions like SSR, routing, and app architecture. It doesn't provide pre-configured flows or templates, which can make the initial setup a bit complex for some teams #### Developer experience Vike's routing system is based on convention but without rigid folder rules. For a **/blog** page, you might have: With this structure, here's what the server logic would look like: And the corresponding component:

    Latest Posts

      {posts.map((post) => (
    • {post.title}
    • ))}
    ); }`} lang="tsx" title={`page.tsx`} /> This works similarly to Remix's `loader()`, but everything is fully explicit. If you want control without compromise and you're comfortable filling in the blanks yourself, Vike is a powerful alternative to Next.js. ### Vite + React Router Vite + React Router [Vite](https://vite.dev/) + [React Router](https://reactrouter.com/) is another modern setup that many developers are sleeping on. While it's not branded as a framework, it gives you everything you need to build fast, maintainable React apps without the overhead. Vite handles your build tooling with speed and precision, and React Router gives you flexible routing with support for nested routes, loaders, and data fetching. #### Features * **Modern build tooling with Vite –** Instant dev server startup, lightning-fast HMR, first-class TypeScript and JSX support, and built-in code splitting * **Data-aware routing with React Router –** Since v6.4, React Router supports route-level loader functions that fetch data before rendering #### Drawbacks * **No file-based routing –** Routes are defined manually in code; while this is flexible, it's more verbose compared to frameworks like Next.js or Remix #### Developer experience Here's a basic setup using React Router's `useLoaderData()` API for route-level data fetching: ); // routes.tsx import Home from './pages/Home'; import Blog from './pages/Blog'; export default [ { path: '/', element: , }, { path: '/blog', element: , loader: async () => { const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5'); if (!res.ok) throw new Response('Failed to load', { status: res.status }); return res.json(); }, }, ]; `} lang="tsx" title={`main.tsx`} /> And then the page itself:

    Latest Posts

      {posts.map((post) => (
    • {post.title}
    • ))}
    ); }`} lang="tsx" title={`blog.tsx`} /> This setup gives you declarative, route-scoped data loading, similar to Next.js or Remix, without adopting a larger framework. ### Which Next.js alternative should I use? Picking the right framework/setup mostly depends on what you're building and how much control, performance, or simplicity you want. Here's how to decide which one makes sense for your project: * **Use Remix** if you want a full-stack React framework with server-side rendering, smart data handling (via loaders and actions), and fewer abstractions. It's great for complex web apps where you want solid defaults but also full control * **Use Astro** if you're building a content-heavy site, like a blog, documentation, or marketing page, and you want raw speed. It ships almost no JavaScript by default and gives you HTML-first authoring with optional React components * **Use TanStack Start** if you're working on a data-heavy app and care deeply about things like type safety, cache management, and precise fetch control. It's early in its development, but it's backed by the team behind TanStack Query. Even if the wrapper's new, the internals are rock solid * **Use Vike** if you want full control over everything, i.e., routing, SSR, data loading, and rendering, without any framework opinions getting in the way. It's more of a toolkit than a framework * **Use Vite + React Router** if you want a fast, lightweight React app without any full-stack ambitions. This setup is great for SPAs, and when you just need modern tooling and solid routing ## Conclusion Next.js is still a powerful framework, but it's no longer the only option for building React apps. If you're feeling boxed in by Vercel's ecosystem or overwhelmed by the constant API changes, you're not alone. This article highlights some of the top alternatives worth exploring if you want more control over your stack. Thanks for reading! # Welcome to mewis.me URL: /blog/others/welcome Portfolio Website — Inspired by tailwindcss.com (v4) *** title: Welcome to mewis.me description: Portfolio Website — Inspired by tailwindcss.com (v4) date: 2025-06-17 parent: others tags: \['welcome'] ------------------ mewis.me is my minimal portfolio website, showcasing my work and experience as a Software Developer & UI/UX Designer. It is built with [Next.js](https://nextjs.org), [Tailwind CSS](https://tailwindcss.com), and [shadcn/ui](https://ui.shadcn.com) to deliver a modern, fast, and seamless user experience. This is a continuously evolving side project where I experiment with new technologies and keep it updated with the latest advancements in web development. ## Core Features & Technologies * **Elegant & Minimalistic UI**: Clean and modern design * **Dark Mode**: Supports light and dark themes for a better user experience * **SEO Optimization**: [JSON-LD schema](https://json-ld.org), sitemap, robots * **Email Protection**: Obfuscation to prevent spam * Installable PWA * [Next.js 15](https://nextjs.org/): Latest React framework for optimized performance and scalability * [Tailwind CSS v4](https://tailwindcss.com): Modern utility-first CSS framework for styling ## Blog Features * MDX & Markdown support * Syntax Highlighting for better readability * RSS Feed for easy content distribution * Dynamic OG Images for rich previews # Consider using AbortController in JavaScript and React URL: /blog/reactjs/consider-using-abortcontroller-in-javascript-and-react Use JavaScript's AbortController in React to manage fetch requests, event listeners, and prevent memory leaks, enhancing performance *** title: "Consider using AbortController in JavaScript and React" description: Use JavaScript's AbortController in React to manage fetch requests, event listeners, and prevent memory leaks, enhancing performance date: 2025-06-17 parent: reactjs tags: \['reactjs', 'abortcontroller'] author: name: Mew url: [https://mewis.me](https://mewis.me) ----------------------------------------- ## Introduction `AbortController` is a JavaScript API introduced in 2017 as part of the Fetch API. It provides a simple and effective way to manage and cancel asynchronous operations, like `fetch` requests and event listeners, particularly when working with React components that require cleanup upon unmounting. In this article, we'll explore how to use `AbortController` for: * Managing and canceling `fetch` requests * Cleaning up multiple event listeners in a React component * Avoiding memory leaks by efficiently handling component unmounts ### What is `AbortController`? `AbortController` allows you to create an abortable signal that can be passed to asynchronous operations, such as `fetch` requests. When you call the `.abort()` method, any asynchronous tasks associated with the signal are canceled immediately. **Example** response.json()) .then(data => console.log(data)) .catch(error => { if (error.name === 'AbortError') { console.log('Fetch request canceled'); } else { console.error('Fetch error:', error); } }); // To cancel the fetch request controller.abort();`} lang="ts" title="abortcontroller.ts" /> #### Use Case 1: Cancelling a Fetch Request in React with `AbortController` In React, it's common to initiate `fetch` requests in `useEffect` to fetch data when a component mounts. With `AbortController`, you can cancel the request when the component unmounts, avoiding unnecessary network usage and potential memory leaks. Here's a React example that fetches data and cancels the request if the component unmounts before it completes: { const controller = new AbortController(); const { signal } = controller; fetch('https://jsonplaceholder.typicode.com/posts', { signal }) .then(response => response.json()) .then(data => setData(data)) .catch(error => { if (error.name === 'AbortError') { console.log('Fetch request was canceled'); } else { console.error('Fetch error:', error); } }); // Cleanup function to cancel the fetch request on component unmount return () => controller.abort(); }, []); return (

    Data

    {data ?
    {JSON.stringify(data, null, 2)}
    : 'Loading...'}
    ); } export default DataFetchingComponent;`} lang="tsx" title="datafetching.tsx" /> In this example: * The `AbortController` is created and passed to the `fetch` request. * When the component unmounts, the cleanup function cancels the ongoing fetch request by calling `controller.abort()`. #### Use Case 2: Setting a Timeout for a Fetch Request with `AbortController` { const controller = new AbortController(); const { signal } = controller; const timeout = 5000; // Set timeout in milliseconds (e.g., 5000ms = 5 seconds) // Set a timeout to abort the fetch request const timeoutId = setTimeout(() => controller.abort(), timeout); // Fetch request with abort signal fetch('https://jsonplaceholder.typicode.com/posts', { signal }) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.json(); }) .then(data => setData(data)) .catch(error => { if (error.name === 'AbortError') { setError('Request timed out'); } else { setError('Fetch error: ' + error.message); } }) .finally(() => clearTimeout(timeoutId)); // Clear the timeout when fetch completes // Cleanup to ensure the timeout and fetch are both cleared on unmount return () => { clearTimeout(timeoutId); controller.abort(); }; }, []); return (

    Data with Timeout

    {error &&

    {error}

    } {data ?
    {JSON.stringify(data, null, 2)}
    : 'Loading...'}
    ); } export default DataFetcherWithTimeout;`} lang="tsx" title="datafetcherwithtimeout.tsx" /> **Explanation of the Code** 1. **Set a Timeout**: `const timeoutId = setTimeout(() => controller.abort(), timeout);` sets a timeout that will automatically call `controller.abort()` after the specified time (5 seconds in this example). 2. **Fetch with Abort Signal**: The `fetch` request includes the `signal` from the `AbortController`, enabling it to listen for an abort event. 3. **Timeout Cleanup**: We use `.finally()` to ensure `clearTimeout(timeoutId)` is called once the fetch completes, regardless of whether it succeeded, failed, or was aborted. 4. **Abort on Unmount**: In the `useEffect` cleanup function, both `clearTimeout(timeoutId)` and `controller.abort()` are called. This is crucial to avoid memory leaks if the component unmounts while the fetch is still pending. 5. **Error Handling**: If the fetch request is aborted, we check for the error type ([`error.name`](http://error.name) `=== 'AbortError'`) to set a custom error message indicating that the request timed out. As of recent updates in the JavaScript ecosystem, there's a proposed `AbortController.timeout()` utility that simplifies setting timeouts with fetch requests. { // Set a 5-second timeout for the request const controller = new AbortController(); controller.timeout = 5000; // 5000 ms = 5 seconds // Fetch request with abort signal fetch('https://jsonplaceholder.typicode.com/posts', { signal: controller.signal }) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.json(); }) .then(data => setData(data)) .catch(error => { if (error.name === 'AbortError') { setError('Request timed out'); } else { setError('Fetch error: ' + error.message); } }); // Cleanup to abort fetch on unmount return () => controller.abort(); }, []); return (

    Data with Timeout

    {error &&

    {error}

    } {data ?
    {JSON.stringify(data, null, 2)}
    : 'Loading...'}
    ); } export default DataFetcherWithTimeout;`} lang="tsx" title="datafetcherwithtimeout.tsx" /> Benefits of Using `AbortController` for Fetch Timeouts * **Improved User Experience**: Avoids making users wait indefinitely if a server is unresponsive. * **Resource Management**: Cancels requests that are no longer needed, reducing network overhead. * **Cleaner Code**: Centralized timeout management through `AbortController` leads to more maintainable code. #### Use Case 3: Using `AbortController` with `WritableStream` In this example, we'll see how you might use `AbortController` to cancel writing to a stream. `WritableStream` is often used when working with large or continuous data streams, allowing you to handle and process data as it arrives. If needed, you can abort the stream mid-operation. { const controller = new AbortController(); // Sample writable stream const writableStream = new WritableStream({ start(controller) { console.log("Stream started"); }, write(chunk, controller) { console.log("Writing chunk:", chunk); }, close() { console.log("Stream closed"); }, abort(reason) { console.error("Stream aborted:", reason); } }); // Simulate writing to the stream const writer = writableStream.getWriter(); writer.write("First chunk of data"); // Abort stream after a delay setTimeout(() => { controller.abort("Timeout exceeded while writing to stream"); }, 3000); // Cleanup on component unmount return () => { writer.releaseLock(); writableStream.abort("Component unmounted"); }; }, []); return
    Stream Writing in Progress...
    ; } export default StreamWriter;`} lang="tsx" title="streamwriter.tsx" /> **Explanation** * **Writable Stream Setup**: We create a `WritableStream` with handlers for `start`, `write`, `close`, and `abort`. * **Abort the Stream**: After a 3-second timeout, the controller aborts the stream. This triggers the `abort` function within the `WritableStream`, logging the reason. * **Cleanup on Unmount**: The `writer.releaseLock()` releases the lock on the `WritableStream` writer, and `writableStream.abort()` ensures any unfinished streams are properly aborted when the component unmounts. #### Use Case 4: Using the `reason` Property with `AbortController` The `reason` property on `AbortController` provides a way to specify a reason for aborting an operation, which can be useful for debugging and logging. { const controller = new AbortController(); const reason = "Request aborted by the user"; // Custom abort reason fetch('https://jsonplaceholder.typicode.com/posts', { signal: controller.signal }) .then(response => response.json()) .then(data => setData(data)) .catch(err => { if (err.name === 'AbortError') { setError(\`Fetch aborted: \${controller.signal.reason || reason}\`); } else { setError('Fetch error: ' + err.message); } }); // Simulate an abort action after 2 seconds setTimeout(() => { controller.abort(reason); }, 2000); return () => controller.abort("Component unmounted"); }, []); return (

    Data Fetch with Abort Reason

    {error &&

    {error}

    } {data ?
    {JSON.stringify(data, null, 2)}
    : 'Loading...'}
    ); } export default FetchWithAbortReason;`} lang="tsx" title="fetchwithabortreason.tsx" /> **Explanation** * **Setting** `reason`: We define a custom abort `reason`, `"Request aborted by the user"`, which provides context for why the request was canceled. * **Abort with Reason**: When `controller.abort(reason)` is called, the `reason` is passed along with the abort signal, which can then be logged or displayed. **Output** If the abort is triggered, the error message will display the `reason` as `"Fetch aborted: Request aborted by the user"`, making it clear why the operation was stopped. #### Use Case 5: Managing Multiple Event Listeners in React with `AbortController` You can also use `AbortController` to manage multiple event listeners efficiently. This is particularly useful in scenarios where multiple listeners are added, and you want to clean them up all at once. { const controller = new AbortController(); const { signal } = controller; // Event handler functions const handleClick = () => console.log('Window clicked'); const handleMouseMove = () => console.log('Mouse moved'); // Add event listeners with AbortController signal window.addEventListener('click', handleClick, { signal }); window.addEventListener('mousemove', handleMouseMove, { signal }); // Cleanup function to remove event listeners on unmount return () => controller.abort(); }, []); return (

    Event Listener Component

    Check the console and interact with the window to see the event logs.

    ); } export default EventListenerComponent;`} lang="tsx" title="eventlistenercomponent.tsx" /> In this example: * We add two event listeners (`click` and `mousemove`) to the `window`. * By attaching the same `signal` to both listeners, we can cancel all listeners in one line with `controller.abort()` when the component unmounts. ### Advantages of Using `AbortController` in React * **Efficient Cleanup**: Using `AbortController` in React's `useEffect` cleanup function allows you to efficiently remove event listeners or cancel async tasks, reducing the risk of memory leaks. * **Centralized Control**: By using a single `AbortController` instance for multiple operations, you gain centralized control, making your code easier to manage and understand. * **Avoiding Unwanted State Updates**: If a request completes after the component has unmounted, trying to set state would lead to an error. `AbortController` prevents such scenarios by canceling tasks that are no longer needed. ### When to Use `AbortController` in React 1. **API Requests**: Use `AbortController` when fetching data that might not complete by the time a component unmounts. 2. **Event Listeners**: Use it to manage and clean up multiple event listeners on the `window` or `document` efficiently. ## Conclusion AbortController is a powerful tool for managing asynchronous operations in JavaScript, especially within React applications. By using it to cancel fetch requests and clean up event listeners, you can prevent memory leaks and improve application performance. Implementing AbortController ensures that your components remain efficient, even when they frequently mount and unmount. Start incorporating it into your React projects to handle asynchronous tasks more effectively and keep your codebase clean and optimized.