Individual page.tsx files for each value of dynamic route param [tab]
Unanswered
Spinone Italiano posted this in #help-forum
Spinone ItalianoOP
Say I have the folder structure
/app/[tab]/
- layout.tsx
- page.tsx
In order to encapsulate logic, it would be nice to have a page.tsx for each value of
/app/[tab]/
- songs.tsx
- artists.tsx
- playlists.tsx
Where
I've come up with this.
Is this the way to solve this problem? Is it bad practice for some reason?
/app/[tab]/
- layout.tsx
- page.tsx
In order to encapsulate logic, it would be nice to have a page.tsx for each value of
tab
./app/[tab]/
- songs.tsx
- artists.tsx
- playlists.tsx
Where
songs, artists, playlists
are all values of tab
.I've come up with this.
// /app/[tab]/page.tsx
import SearchArtistsPage from "./artists";
import SearchAlbumsPage from "./albums";
import SearchPlaylistsPage from "./playlists";
import SearchSongsPage from "./songs";
export default async function SearchPage({ searchParams, params }) {
const { tab } = await params;
return (
<>
{tab === 'songs' && <SearchSongsPage searchParams={searchParams} params={params} />}
{tab === 'albums' && <SearchAlbumsPage searchParams={searchParams} params={params} />}
{tab === 'playlists' && <SearchPlaylistsPage searchParams={searchParams} params={params} />}
{tab === 'artists' && <SearchArtistsPage searchParams={searchParams} params={params} />}
</>
)
}
Is this the way to solve this problem? Is it bad practice for some reason?
12 Replies
// /app/[tab]/page.tsx
import SearchArtistsPage from "./artists";
import SearchAlbumsPage from "./albums";
import SearchPlaylistsPage from "./playlists";
import SearchSongsPage from "./songs";
export default async function SearchPage({ searchParams, params }) {
const { tab } = await params;
return (
<>
{tab === 'songs' && <SearchSongsPage searchParams={searchParams} params={params} />}
{tab === 'albums' && <SearchAlbumsPage searchParams={searchParams} params={params} />}
{tab === 'playlists' && <SearchPlaylistsPage searchParams={searchParams} params={params} />}
{tab === 'artists' && <SearchArtistsPage searchParams={searchParams} params={params} />}
</>
)
}
This would be a way but you will need some sort of catch all at the end to redirect to not found.
Or if they are the inly for vakues they don't need dynamic routing.
Just create:
app/songs/page.tsx
app/artists/page.tsx
etc
Sometimes its easy to over complicate stuff.
The [something] functionality is only needed for if you have no idea what that would be like for example a blog slug or something
Spinone ItalianoOP
Thank you!
My bad I forgot to mention something crucial. All of these share the same
In the case where I have them in separate folders, I don't get the
My bad I forgot to mention something crucial. All of these share the same
layout.tsx
which displays a list of tabs and highlights the currently selected tab, so it needed the [tab]
param.In the case where I have them in separate folders, I don't get the
tab
value for the layout.app/songs/page.tsx
app/artists/page.tsx
Show me your layout code.
Spinone ItalianoOP
// app/[tab]/layout.tsx
import SearchResultTabs from "@/src/components/SearchResultTabs";
export default function SearchTabLayout({ children, params }) {
return (
<section>
<SearchResultTabs />
{children}
</section>
)
}
// @/src/components/SearchResultTabs
'use client'
import { Tabs } from "@mantine/core";
import { redirect, useParams, useRouter } from "next/navigation";
export default function SearchResultTabs() {
const router = useRouter();
const { search, tab } = useParams();
return (
<Tabs
// keepMounted={true}
defaultValue={tab || 'songs'}
onChange={(tab) => {
router.push(`/search/${search}/${tab}`);
}}
>
<Tabs.List>
<Tabs.Tab value="songs" >
Songs
</Tabs.Tab>
<Tabs.Tab value="playlists" >
Playlists
</Tabs.Tab>
<Tabs.Tab value="artists" >
Artists
</Tabs.Tab>
<Tabs.Tab value="albums" >
Albums
</Tabs.Tab>
</Tabs.List>
</Tabs>
)
}
As if it hink you means the same thing. I use this in my sidebar for showing the active page:
"use client";
import Link from "next/link";
import {usePathname} from "next/navigation";
import LogoComponent from "@/components/common/logo/LogoComponent";
const Sidebar = () => {
const pathname = usePathname();
// Helper function to compute class names.
// For the dashboard link, require an exact match.
const navItemClass = (href: string): string => {
if (href === "/dashboard") {
return pathname === href
? "block p-2 rounded bg-sky-300 text-slate-900"
: "block p-2 rounded text-gray-700 dark:text-slate-300 hover:bg-sky-100 dark:hover:bg-sky-800";
}
// For other links, use startsWith.
return pathname.startsWith(href)
? "block p-2 rounded bg-sky-300 text-slate-900"
: "block p-2 rounded text-gray-700 dark:text-slate-300 hover:bg-sky-100 dark:hover:bg-sky-800";
};
return (
<aside className="h-full w-64 border-r border-gray-200 bg-white p-4 dark:border-slate-700 dark:bg-slate-800">
<div className="mb-6">
<LogoComponent className="mx-auto h-12 w-auto" />
</div>
<nav>
<ul className="space-y-2">
<li>
<Link
href="/dashboard"
className={navItemClass("/dashboard")}
>
Dashboard
</Link>
</li>
<li>
<Link
href="/dashboard/posts"
className={navItemClass("/dashboard/posts")}
>
Posts
</Link>
</li>
<li>
<Link
href="/dashboard/categories"
className={navItemClass("/dashboard/categories")}
>
Categories
</Link>
</li>
<li>
<Link
href="/dashboard/tags"
className={navItemClass("/dashboard/tags")}
>
Tags
</Link>
</li>
<li>
<Link
href="/dashboard/authors"
className={navItemClass("/dashboard/authors")}
>
Authors
</Link>
</li>
<li>
<Link
href="/dashboard/gallery"
className={navItemClass("/dashboard/gallery")}
>
Gallery
</Link>
</li>
</ul>
</nav>
</aside>
);
};
export default Sidebar;
And this allows you to have them as individual page.tsx files
This sidebar is imported in my layout FYI
Spinone ItalianoOP
Thank you again!
Yeah I think we are essentially doing the same thing.
Your way uses string parsing of the pathname, mine uses the dynamic route param with imports for each page.
I guess I was just wondering if there was anything inheritly bad in my method: importing a page in a page.
Anything about caching or something that makes the page components special and using them the way I'm doing being wrong...
Yeah I think we are essentially doing the same thing.
Your way uses string parsing of the pathname, mine uses the dynamic route param with imports for each page.
I guess I was just wondering if there was anything inheritly bad in my method: importing a page in a page.
Anything about caching or something that makes the page components special and using them the way I'm doing being wrong...
I’m guessing there would be a slight performance increase in the static page.tsx over the dynamic route. As even though you know the potential options for it next has no built in way of identifying them so I’d say performance would be the benefit but it may negligible!
Spinone ItalianoOP
Intuitively that seems right to me! I'll probably switch to your method for that reason
thanks again for your help
No problem, in the long run will probably lead to cleaner code as you can do your string parsing like me in the layout or nav components! And just focus on the actual page components in the pages! Can sort of forget about it after you’ve wrote it!