Skip to content

Instantly share code, notes, and snippets.

@ahmadrosid
Created August 27, 2023 10:25
Show Gist options
  • Save ahmadrosid/a35c1afc179848f80a5d54dd26011f44 to your computer and use it in GitHub Desktop.
Save ahmadrosid/a35c1afc179848f80a5d54dd26011f44 to your computer and use it in GitHub Desktop.
Vite react shadcn header menu base.

Header component basic for vite with react and typescript project.

Requirement

  • generouted
  • lucide-react
  • ui.shadcn.com
import { Header } from "@/components/header";
import { Outlet, useRouteError } from "react-router-dom";
import { ThemeProvider } from "@/components/theme-provider";
import { QueryClient, QueryClientProvider } from "react-query";
import { Toaster } from "sonner";
export const Catch = () => {
const error = useRouteError();
console.error(error);
return (
<div className="flex justify-center p-8">🥺 Something went wrong...!</div>
);
};
export const Pending = () => <div>Loading from _app...</div>;
export default function App() {
const queryClient = new QueryClient();
return (
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<QueryClientProvider client={queryClient}>
<Header />
<main>
<Outlet />
</main>
<Toaster richColors />
</QueryClientProvider>
</ThemeProvider>
);
}
import { Button } from "@/components/ui/button";
import { Wand2 } from "lucide-react";
import { Link } from "@/router";
import { ModeToggle } from "./mode-toggle";
export function Header() {
return (
<header className="supports-backdrop-blur:bg-background/60 sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur dark:bg-gray-900/75 dark:border-gray-800">
<div className="pl-8 pr-8 flex h-14 items-center">
<div className="mr-4 flex flex-1">
<nav className="flex items-center space-x-2">
<p className="font-bold flex items-center gap-3">
<Wand2 className="w-5 h-5" />
<Link to="/" className="text-lg">
StorySprinkle
</Link>
</p>
<Link to="/">
<Button className="dark:text-white text-gray-800" variant="link">
Home
</Button>
</Link>
<Link to="/">
<Button className="dark:text-white text-gray-800" variant="link">
Saved
</Button>
</Link>
</nav>
</div>
<div className="hidden md:flex flex-1 items-center justify-between space-x-2 md:justify-end">
<nav className="flex items-center">
{/* Your next menu here */}
<div className="px-4">
<ModeToggle />
</div>
</nav>
</div>
<div className="px-4 block md:hidden">
<ModeToggle />
</div>
</div>
</header>
);
}
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/components/theme-provider";
export function ModeToggle() {
const { theme, setTheme } = useTheme();
return (
<Button
variant="outline"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
);
}
import { createContext, useContext, useEffect, useState } from "react";
type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: string;
storageKey?: string;
};
type ThemeProviderState = {
theme: string;
setTheme: (theme: string) => void;
};
const initialState = {
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState(
() => localStorage.getItem(storageKey) || defaultTheme
);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: string) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment