Extend Your DevTools
DevTools was designed for extensibility, allowing you to add your own components and development tools.
In this guide, we'll show you how to extend DevTools to add a dev bypass feature for role-based authentication.
Create a new file called dev-tools.tsx with the following content:
"use client"; /* Add the "use client" directive if you're using Next.js */
import { DevTools as SuspenseDevTools } from 'suspense-fallback-debugger';
export function DevTools() {
return (
<SuspenseDevTools/>
);
};
Update your layout to replace the default DevTools with your custom one:
import { DevTools } from "@/components/dev-tools"; /* Or wherever you have it */
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
{children}
<DevTools />
</body>
</html>
);
}
This displays the same DevTools as before, but since it's in a separate file, you can easily customize it to fit your needs and use client components.
Add a component named DevBypass to extend it:
"use client"
import {
DropdownMenuGroup,
DropdownMenuGroupLabel,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuPortal,
} from "suspense-fallback-debugger/dropdown-menu";
import { useState, useEffect } from 'react'
/**
* Your auth logic
*/
import { useSession, Role, type RoleType } from '@/auth'
import { setDevSessionRole, getDevSessionRole } from '@/auth/__dev__'
function DevBypass() {
const { session } = useSession();
const [role, setRole] = useState<RoleType | undefined>(undefined);
const handleRoleChange = async (role: RoleType) => {
await setDevSessionRole(role);
setRole(role);
};
useEffect(() => {
getDevSessionRole().then((role) => {
if (!role) {
setRole(session?.user.role);
return;
}
setRole(role);
});
}, [session?.user]);
return (
<DropdownMenuGroup>
<DropdownMenuGroupLabel>Debug</DropdownMenuGroupLabel>
<DropdownMenuSub>
<DropdownMenuSubTrigger>Role</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuGroup>
<DropdownMenuLabel>Set Role As</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup
value={role}
onValueChange={(role) => handleRoleChange(role as RoleType)}
>
<DropdownMenuRadioItem value={Role.ADMIN}>
Admin
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value={Role.USER}>
User
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
);
}
All dropdown menu components are exported from suspense-fallback-debugger/dropdown-menu. These components are the same as those from Shadcn UI. See the Shadcn DropdownMenu documentation for more information.
Add this to your DevTools as its children:
"use client";
import { DevTools as SuspenseDevTools } from 'suspense-fallback-debugger';
import { DevBypass } from '@/components/__dev__/dev-bypass'
export function DevTools() {
return (
<SuspenseDevTools>
<DevBypass/>
</SuspenseDevTools>
);
};
Since the dropdown only renders in development environments, you can add as many dev tools as needed to improve your development experience.
Troubleshooting
Why Can't I Use My Shadcn Dropdown Menu to Extend My Dev Dropdown Menu?
Even if you've already installed the dropdown menu from Shadcn UI, you can't use it to extend your dev dropdown menu. This is due to a conflict with the Context API. Dropdown menu components search for the nearest context provider, and since they're imported from different packages, the context can't match and will throw an error. That's why we also export the dropdown menu components.