The SuperTokens error “Uncaught Error: Cannot use useSessionContext outside auth wrapper components” is caused by trying to use useSessionContext
outside the SuperTokensWrapper
component. To fix this, refactor your code so that all components that need to use useSessionContext
are located within the SuperTokensWrapper
tags.
This error was discovered while I was working on the SuperTokens example. If you’re just getting into SuperTokens, I recommend you check out that article and its associated GitHub project.
Let’s look at a scenario that can cause this issue.
Problem: you’re using useSessionContext outside the SuperTokensWrapper
Below is some code from our SuperTokens example article:
import { Provider } from "urql";
import { urqlClient } from "./utils";
import SuperTokens from "supertokens-auth-react";
import { SuperTokensWrapper, getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import { signOut } from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";
import { SessionAuth, useSessionContext } from 'supertokens-auth-react/recipe/session';
import {
BrowserRouter,
Routes,
Route,
Link,
useNavigate,
} from "react-router-dom";
import * as reactRouterDom from "react-router-dom";
import HomePage from "./HomePage";
SuperTokens.init({
appInfo: {
appName: "example",
apiDomain: `http://${process.env.REACT_APP_HOSTNAME}:8000`,
websiteDomain: `http://${process.env.REACT_APP_HOSTNAME}:3000`,
apiBasePath: "/auth",
websiteBasePath: "/auth",
},
recipeList: [
EmailPassword.init(),
Session.init()
]
});
function App() {
const {loading, doesSessionExist} = useSessionContext();
const navigate = useNavigate();
const onSignOut = async () => {
await signOut();
navigate("/auth");
}
return (
<SuperTokensWrapper>
<Provider value={urqlClient}>
<BrowserRouter>
<div id="main">
<div id="menu">
<ul>
<li><Link to="/">Home</Link></li>
<li>
{ !loading &&
doesSessionExist
? <button onClick={onSignOut}>Sign Out</button>
: <Link to="/auth">Sign In</Link>
}
</li>
</ul>
</div>
<Routes>
<Route path="/" element={
<SessionAuth>
<HomePage />
</SessionAuth>
} />
{getSuperTokensRoutesForReactRouterDom(reactRouterDom)}
</Routes>
</div>
</BrowserRouter>
</Provider>
</SuperTokensWrapper>
);
}
export default App;
JavaScriptThis is the top level App.js
file that is generated by create-react-app
. It’s usually a good place to put the components and configuration needed for the various plug-ins your app uses, as well as the top-level layout such as the Routes
tags if you’re using react-router-dom
, the menu, etc.. Normally that’s the case, but when running this app in a browser, I got a blank page and the following error in the dev console:
This took a while to figure out, but the problem ended up being the following lines:
function App() {
const {loading, doesSessionExist} = useSessionContext();
const navigate = useNavigate();
const onSignOut = async () => {
await signOut();
navigate("/auth");
}
return (
<SuperTokensWrapper>
<Provider value={urqlClient}>
<BrowserRouter>
<div id="main">
<div id="menu">
<ul>
<li><Link to="/">Home</Link></li>
<li>
{ !loading &&
doesSessionExist
? <button onClick={onSignOut}>Sign Out</button>
: <Link to="/auth">Sign In</Link>
}
</li>
</ul>
</div>
<Routes>
<Route path="/" element={
<SessionAuth>
<HomePage />
</SessionAuth>
} />
{getSuperTokensRoutesForReactRouterDom(reactRouterDom)}
</Routes>
</div>
</BrowserRouter>
</Provider>
</SuperTokensWrapper>
);
}
export default App;
JavaScriptIt seems obvious in hindsight, but you can’t use useSessionContext
from outside the SuperTokensWrapper
tags because there’s no context for it to access.
Next we’ll look at how I refactored the code to fix the error.
Solution: move your component that calls useSessionContext inside the SuperTokensWrapper
As I already mentioned, we need to put our useSessionContext
hook inside the SuperTokensWrapper
tag. Unfortunately, this means we need another top-level component besides App.js
. I chose to call it Main.js
.
Here’s our new App.js
file:
import { Provider } from "urql";
import { urqlClient } from "./utils";
import { BrowserRouter } from "react-router-dom";
import SuperTokens from "supertokens-auth-react";
import EmailPassword from "supertokens-auth-react/recipe/emailpassword";
import Session from "supertokens-auth-react/recipe/session";
import { SuperTokensWrapper } from "supertokens-auth-react";
import Main from "./Main";
SuperTokens.init({
appInfo: {
appName: "example",
apiDomain: `http://${process.env.REACT_APP_HOSTNAME}:8000`,
websiteDomain: `http://${process.env.REACT_APP_HOSTNAME}:3000`,
apiBasePath: "/auth",
websiteBasePath: "/auth",
},
recipeList: [
EmailPassword.init(),
Session.init()
]
});
function App() {
return (
<SuperTokensWrapper>
<Provider value={urqlClient}>
<BrowserRouter>
<Main />
</BrowserRouter>
</Provider>
</SuperTokensWrapper>
);
}
export default App;
App.jsAs you can see, we took out the useSessionContext
hook and all associated stuff and placed it in Main.js
so that it will actually work.
Here’s Main.js
:
import { getSuperTokensRoutesForReactRouterDom } from "supertokens-auth-react";
import { signOut } from "supertokens-auth-react/recipe/emailpassword";
import { SessionAuth, useSessionContext } from 'supertokens-auth-react/recipe/session';
import {
Routes,
Route,
Link,
useNavigate,
} from "react-router-dom";
import * as reactRouterDom from "react-router-dom";
import HomePage from "./HomePage";
export default function Main() {
const {loading, doesSessionExist} = useSessionContext();
const navigate = useNavigate();
const onSignOut = async () => {
await signOut();
navigate("/auth");
}
return (
<div id="main">
<div id="menu">
<ul>
<li><Link to="/">Home</Link></li>
<li>
{ !loading &&
doesSessionExist
? <button onClick={onSignOut}>Sign Out</button>
: <Link to="/auth">Sign In</Link>
}
</li>
</ul>
</div>
<Routes>
<Route path="/" element={
<SessionAuth>
<HomePage />
</SessionAuth>
} />
{getSuperTokensRoutesForReactRouterDom(reactRouterDom)}
</Routes>
</div>
);
}
Main.jsThis does add some more lines of code to your app, but it makes it possible to use useSessionContext
, and makes sure that the rest of your components actually exist within the context of SuperTokensWrapper
.
Conclusion
The SuperTokens React SDK requires that all calls to useSessionContext
occur from inside the SuperTokensWrapper
tags. This is necessary because, if you don’t, there’s no context for the hook to use. The context is SuperTokensWrapper
. Fixing this error usually means refactoring your top-level component, be it App.js
if you’re using create-react-app
or otherwise, and creating another “top” level component where your actual app logic resides.
John is a professional software engineer who has been solving problems with code for 15+ years. He has experience with full stack web development, container orchestration, mobile development, DevOps, Windows and Linux kernel development, cybersecurity, and reverse engineering. In his spare time, he’s researching the potential business applications of AI.