
✅ Layout
<routes.ts>
...prefix("/:productId", [
index("features/products/pages/product-redirect-page.tsx"),
layout("features/products/layouts/product-overview-layout.tsx", [
route("/overview", "features/products/pages/product-overview-page.tsx"),
...prefix("/reviews", [
index("features/products/pages/product-reviews-page.tsx"),
route("/new", "features/products/pages/new-product-review-page.tsx"),
]),
]),
]),
layout fn을 사용하여 layout 파일을 지정하고 해당 route내에 Outlet으로 rendering할 children route들을 넣으면 된다.
<product-overview-layout.tsx>
import { ChevronUpIcon, StarIcon } from "lucide-react";
import { Link, Outlet } from "react-router";
import { Button } from "~/common/components/ui/button";
export default function ProductOverviewLayout() {
return (
<div className="space-y-10">
<div className="flex justify-between">
<div className="flex gap-10">
<div className="bg-primary/50 size-40 rounded-xl shadow-xl"></div>
<div>
<h1 className="text-5xl font-bold">Product Name</h1>
<p className="text-2xl font-light">Product description</p>
<div className="mt-5 flex items-center gap-2">
<div className="flex text-yellow-400">
{Array.from({ length: 5 }).map((_, index) => (
<StarIcon className="size-4" fill="currentColor" />
))}
</div>
<span className="text-muted-foreground">100 reviews</span>
</div>
</div>
</div>
<div className="flex gap-5">
<Button
variant={"secondary"}
size={"lg"}
className="h-14 px-10 text-lg"
>
Visit website
</Button>
<Button size={"lg"} className="h-14 px-10 text-lg">
<ChevronUpIcon className="size-4" />
Upvote (100)
</Button>
</div>
</div>
<div className="flex gap-2">
<Button variant={"outline"} asChild>
<Link to={`/products/${productId}/overview`}>Overview</Link>
</Button>
<Button variant={"outline"} asChild>
<Link to={`/products/${productId}/reviews`}>Reviews</Link>
</Button>
</div>
<div>
<Outlet />
</div>
</div>
);
}
✅ NavLink
import { ChevronUpIcon, StarIcon } from "lucide-react";
import { NavLink, Outlet } from "react-router";
import { Button, buttonVariants } from "~/common/components/ui/button";
import { cn } from "~/lib/utils";
export default function ProductOverviewLayout() {
return (
<div className="space-y-10">
<div className="flex justify-between">
<div className="flex gap-10">
<div className="bg-primary/50 size-40 rounded-xl shadow-xl"></div>
<div>
<h1 className="text-5xl font-bold">Product Name</h1>
<p className="text-2xl font-light">Product description</p>
<div className="mt-5 flex items-center gap-2">
<div className="flex text-yellow-400">
{Array.from({ length: 5 }).map((_, index) => (
<StarIcon
key={index}
className="size-4"
fill="currentColor"
/>
))}
</div>
<span className="text-muted-foreground">100 reviews</span>
</div>
</div>
</div>
<div className="flex gap-5">
<Button
variant={"secondary"}
size={"lg"}
className="h-14 px-10 text-lg"
>
Visit website
</Button>
<Button size={"lg"} className="h-14 px-10 text-lg">
<ChevronUpIcon className="size-4" />
Upvote (100)
</Button>
</div>
</div>
<div className="flex gap-2">
<NavLink
className={({ isActive }) =>
cn(
buttonVariants({ variant: "outline" }),
isActive && "bg-accent text-foreground",
)
}
to={`/products/productId-0/overview`}
>
Overview
</NavLink>
<NavLink
className={({ isActive }) =>
cn(
buttonVariants({ variant: "outline" }),
isActive && "bg-accent text-foreground",
)
}
to={`/products/productId-0/reviews`}
>
Reviews
</NavLink>
</div>
<div>
<Outlet />
</div>
</div>
);
}
'📂 라이브러리' 카테고리의 다른 글
| 라이브러리 - Magic UI (1) | 2026.05.21 |
|---|---|
| 라이브러리 - Simple Icons (0) | 2026.05.15 |
| 라이브러리 - Luxon (0) | 2026.05.04 |
| 라이브러리 - Lucide React (0) | 2026.04.29 |
| 라이브러리 - Shadcn/ui (0) | 2026.04.27 |