Search code examples
next-authstorybooknextjs-storybook

Mocking next-auth in storybook


I have a NextJS application with an app router that uses NextAuth.js that also includes Storybook.

Within the project, we have a HOC that wraps specific components with the authentication context (cutting out some of the Typescript markup to make it more succinct):

// context/AuthProvider.tsx
import { SessionProvider } from 'next-auth/react';
// ...
export default function AuthProvider({children}) {
  return <SessionProvider>{children}</SessionProvider>
}

// hoc/withAuthProvider.tsx
export const withAuthProvider = (WrappedComponent) => {
  const ComponentWithSP = (props) => {
    <AuthProvider>
      <WrappedComponent {...props} />
    </AuthProvider>
  };
  return ComponentWithSP;
}

When the components are defined, they have:

export default withAuthProvider(MyClientComponent);

I want to write a Storybook story for MyClientComponent but cannot figure out how to mock the session or inject a mocked session.

My basic story looks like this:

import type { Meta, StoryObj } from '@storybook/react';

import MyClientComponent from './MyClientComponent';

const meta: Meta<typeof MyClientComponent> = {
  title: 'MyClientComponent',
  component: MyClientComponent,
};

export default meta;

export const MyClientComponentStory: StoryObj<typeof MyClientComponent> = {
  name: 'MyClientComponent',
};

Grabbing the session I didn't do anything special:

const { data: session, status } = useSession({
  required: true,
  onUnauthenticated() {
    redirect('/api/auth/signin');
  },
});

I found these:

Versions in use:

  • Node: 22.2.0
  • next: 14.2.3
  • next-auth: 4.24.7
  • storybook: 8.1.5

Solution

  • Try the following - just some ideas below...

    Edit .storybook/preview.js to include a custom decorator that wraps your components with the SessionProvider (from NextAuth.js).

    Then try to create a mock session object that reflects the state you want to simulate.

    Use the mocked session in the SessionProvider.

    Quick example (not working - but as an idea):

    // .storybook/preview.js
    import { SessionProvider } from 'next-auth/react';
    
    const mockSession = {
      expires: '2023-01-01T00:00:00Z',
      user: {
        name: 'Storybook User',
        email: '[email protected]',
      },
    };
    
    export const decorators = [
      (Story) => (
        <SessionProvider session={mockSession}>
          <Story />
        </SessionProvider>
      ),
    ];
    

    Now, when you define your Storybook stories:

    // MyComponent.stories.js

    import MyClientComponent from '../components/MyClientComponent';
    
    export default {
      title: 'MyClientComponent',
      component: MyClientComponent,
    };
    
    export const Primary = () => <MyClientComponent />;
    

    Give something like this a try and let me know if this helps you out!