DEV Community

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

Posted on

Fixing Vite HMR Issues in React by Splitting Your Auth Context

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! 👇

Less yak shaving. More shipping. JavaScript approved.

Less yak shaving. More shipping. JavaScript approved.

Kinde gives you the essentials—auth, RBAC, subscriptions—so you can launch fast and scale clean.

Get a free account

Top comments (4)

Collapse
 
rickyryden profile image
Ricky Rydén • Edited

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
Collapse
 
ttibbs profile image
Terry W

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

Collapse
 
zoltan438973634 profile image
Zoltan • Edited

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!! 🙏

Collapse
 
ttibbs profile image
Terry W

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

MongoDB Atlas runs apps anywhere. Try it now.

MongoDB Atlas runs apps anywhere. Try it now.

MongoDB Atlas lets you build and run modern apps anywhere—across AWS, Azure, and Google Cloud. With availability in 115+ regions, deploy near users, meet compliance, and scale confidently worldwide.

Start Free

👋 Kindness is contagious

Explore this insightful write-up embraced by the inclusive DEV Community. Tech enthusiasts of all skill levels can contribute insights and expand our shared knowledge.

Spreading a simple "thank you" uplifts creators—let them know your thoughts in the discussion below!

At DEV, collaborative learning fuels growth and forges stronger connections. If this piece resonated with you, a brief note of thanks goes a long way.

Okay