Fixing Vite HMR Issues in React by Splitting Your Auth Context
Terry W

Terry W @ttibbs

About: Full Stack Developer

Joined:
Jul 15, 2024

Fixing Vite HMR Issues in React by Splitting Your Auth Context

Publish Date: May 20
0 4

Have you ever updated a file in your React + Vite project only to see full page reloads instead of fast refresh? 😩

That happened to me when I built an authentication context and exported both the provider and a custom hook (useAuth) from the same .tsx file. Turns out, that’s a no-go if you want smooth Hot Module Reloading (HMR) with Vite.

Here’s what was happening and how a small refactor completely fixed it.

The Problem

I had a single AuthContext.tsx file that looked something like this:

import {
  createContext,
  useContext,
  useState,
  useEffect,
  ReactNode,
} from "react";
import authApi from "@/api/auth-api";
import { User } from "@/types/users";
import { AuthContextType, AuthResponse } from "@/types/auth";
const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
...
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}
Enter fullscreen mode Exit fullscreen mode

Vite kept logging:

vite hmr invalidate /src/contexts/AuthContext.tsx Could not Fast Refresh ("useAuth" export is incompatible). Learn more at https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#consistent-components-exports

Basically, Vite's React plugin only supports Fast Refresh when your .tsx file exports components only (i.e., PascalCase components). If you export anything else — like a custom hook — it invalidates the module and reloads the whole page.

The Fix

The cleanest solution: split your hook into a separate .ts file.

AuthContext.ts

import { createContext, useContext } from "react";
import type { AuthContextType } from "@/types/auth";

export const AuthContext = createContext<AuthContextType | undefined>(
  undefined
);

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuth must be used within <AuthProvider>");
  return ctx;
}
Enter fullscreen mode Exit fullscreen mode

AuthProvider.tsx

import { useState, useEffect, ReactNode } from "react";
import authApi from "@/api/auth-api";
import { User } from "@/types/users";
import { AuthResponse } from "@/types/auth";
import { AuthContext } from "@/contexts/AuthContext";

export function AuthProvider({ children }: { children: ReactNode }) {
  // ... state + auth methods here ...

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Enter fullscreen mode Exit fullscreen mode

Takeaway

If you're using Vite with React and notice weird HMR behaviour, check your exports. Keep .tsx files component-only, and move hooks or utilities into .ts files to avoid these issues.

Have you run into other quirky Vite or React dev issues? Got any lightweight performance tricks? Drop them in the comments, and let others know what’s worked for you! 👇

Comments 4 total

  • Zoltan
    ZoltanJun 7, 2025

    OMG I've been struggling for days with this, it's been driving me crazy. I've tried so many things, this is the only one that actually worked. Thank you so much!! 🙏

    • Terry W
      Terry WJul 27, 2025

      No problem at all. I'm glad it was useful!

  • Ricky Rydén
    Ricky RydénJul 15, 2025

    I was struggling with this and I could not understand why I still got the error, even after I moved my custom hooks to a /hooks/auth.ts file.

    I didn't want to name the file Auth.ts since it was not a component, and it turns out that the error only occurred when a file starts with lower case.

    So I moved all my hooks to their own files (starting with use, for example: useAuth.ts), and that works without any errors.

    I use this structure, similar code like in your post, but I chose to place hook and context in different files.

    - /hooks/useAuth.ts
    - /contexts/authContext.ts
    - /providers/AuthProvider.tsx
    
    Enter fullscreen mode Exit fullscreen mode
    • Terry W
      Terry WJul 27, 2025

      Thank you for pointing that out, it's something I overlooked when creating the post. But generally, yes, this is a much better way to structure your folders and files

Add comment