Problem with styling nested routes
Answered
Maltese posted this in #help-forum
MalteseOP
Hello, guys! Can you help with that one? I want when I am in the /posts route to make the Posts Link styled and also when I am in a nested link that is a part of the /posts route parent to also make the Posts Link styled
Answered by necm1
https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment - just searched in their docs to get the whole path and underlaying child paths
54 Replies
MalteseOP
MalteseOP
@Maltese Click to see attachment
you can change your inline if statement. Right now you saying if this full route is matching the href, highlight it. Instead, you can say if the route starts with the href, highlight it. You can do that by checking
pathname.startsWith(...)
MalteseOP
I tried it but that opens onother problem
when I am at / (root route), and I go to /posts this triggers the Home and Posts Links, because Home is pointing to '/' and Posts points to '/posts'
and both get styled
@Maltese and both get styled
So, I've some suggestions including the fix for your issue.
@Maltese when I am at / (root route), and I go to /posts this triggers the Home and Posts Links, because Home is pointing to '/' and Posts points to '/posts'
then you can still find the most matching link and the most matching then will be highlighted
MalteseOP
you are talking about making the paths absolute?
this works, yea, but I want to know how I can make it with relative paths
@Maltese you are talking about making the paths absolute?
nah, more like this:
function mostMatchingLink(pathname, links) {
let m = pathname;
let uppercaseLetters = 0;
let matchLength = (str1, str2) => {
let i = 0;
while (i < str1.length && i < str2.length && str1[i] === str2[i]) i++;
return i;
};
let mostMatchingLink = links.reduce((a, b) => {
return matchLength(pathname.toLowerCase(), b.toLowerCase()) > matchLength(pathname.toLowerCase(), a.toLowerCase()) ? b : a;
}, "");
return mostMatchingLink;
}
MalteseOP
pfff that looks a little complicated haha 😂
is this the best option?
@Maltese pfff that looks a little complicated haha 😂
that's a solution how you find the most matching link. Hopefully it looks not too complicated.. I translated it from another (java 💀 ) code
MalteseOP
☠️
1. I would create a new component called like NavSideBarItem, which contains the logic for your link (pass
2. You're just using
3. Use
example code:
link.route
etc. as params to the nested component)2. You're just using
usePathname
which returns the whole path and nested routes wont have the same pathname as their parent3. Use
useSelectedLayoutSgements
including usePathname()
example code:
const NavSideBarItem = ({ route, label }: { route: string, label: string }) => {
const [active, setActive] = useState<boolean>(false);
const selectedLayoutSegment = useSelectedLayoutSegment();
const pathName = usePathname();
useEffect(() => {
setActive(pathName === route);
const splittedRoute = route.split('/');
const routePath = splittedRoute[1];
if (!routePath) {
return;
}
setActive(selectedLayoutSegment === routePath || false);
}, [selectedLayoutSegment, route, pathName]);
return <Link href={route} className={cn('rounded-md text-xl p-2', active && 'bg-slate-200')}>{label}</Link>
}
thats actually how you should do it
MalteseOP
wow I have never heard of useSelectedLayoutSegment before
how you manage to find it?
because from what I've watched from tutorials, they just use .startWith() or something similar
now in your NavSideBar component
{sidebarLinks.map((link, index) => (<NavSideBarItem key={`${link.label}-${index}`} {...link} />))}
@Maltese wow I have never heard of useSelectedLayoutSegment before
https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment - just searched in their docs to get the whole path and underlaying child paths
Answer
@necm1 1. I would create a new component called like NavSideBarItem, which contains the logic for your link (pass `link.route` etc. as params to the nested component)
2. You're just using `usePathname` which returns the whole path and nested routes wont have the same pathname as their parent
3. Use `useSelectedLayoutSgements` including `usePathname()`
example code:
typescript
const NavSideBarItem = ({ route, label }: { route: string, label: string }) => {
const [active, setActive] = useState<boolean>(false);
const selectedLayoutSegment = useSelectedLayoutSegment();
const pathName = usePathname();
useEffect(() => {
setActive(pathName === route);
const splittedRoute = route.split('/');
const routePath = splittedRoute[1];
if (!routePath) {
return;
}
setActive(selectedLayoutSegment === routePath || false);
}, [selectedLayoutSegment, route, pathName]);
return <Link href={route} className={cn('rounded-md text-xl p-2', active && 'bg-slate-200')}>{label}</Link>
}
wouldn't that be the same problem? Because of
setActive(pathName === route);
pathName === route // same as he first did
selectedLayoutSegment === routePath || false // same as he first did
// Navigating to
/blog/hello-world
will return 'hello-world'@necm1 // Navigating to `/blog/hello-world` will return 'hello-world'
and navigating to
/block/hello-world/hello
would also return hello-world
, right?so we're just making sure if we are on the parent, usePathname would take care of it
meanwhile on the child level segments would
mb, took some time
yea, thats a pretty good example from vercel
so in the first place we take care of the parent and if we have the child, we obv. want it to make the parent active in any way
so we take care of everything with this simple useEffect part
@Maltese you are talking about making the paths absolute?
would suggest to remove the domain inside the route path:
because the <Link /> component will take care for internal routes
{
route: '/',
label: 'Home'
},
{
route: '/posts',
label: 'Posts'
}
because the <Link /> component will take care for internal routes
unless you want to redirect the user to an external domain
Original message was deleted
btw. if it worked for you, could you maybe mark my answer as solution described here:
MalteseOP
yea, I will try it soon
MalteseOP
ok I fixed the main layout navigation bar issue, but now I don't know how to check the path into the posts layout nav bar
now when I am on posts/create, the posts Link is styled and thats great, but the Create Link is not
what should I use
usePathname() and .split('/')?
or there is a better way
didn't see these on top
you could maybe just write again a new TopNavbarItem
and then use LayoutSegemnts
@necm1 1. I would create a new component called like NavSideBarItem, which contains the logic for your link (pass `link.route` etc. as params to the nested component)
2. You're just using `usePathname` which returns the whole path and nested routes wont have the same pathname as their parent
3. Use `useSelectedLayoutSgements` including `usePathname()`
example code:
typescript
const NavSideBarItem = ({ route, label }: { route: string, label: string }) => {
const [active, setActive] = useState<boolean>(false);
const selectedLayoutSegment = useSelectedLayoutSegment();
const pathName = usePathname();
useEffect(() => {
setActive(pathName === route);
const splittedRoute = route.split('/');
const routePath = splittedRoute[1];
if (!routePath) {
return;
}
setActive(selectedLayoutSegment === routePath || false);
}, [selectedLayoutSegment, route, pathName]);
return <Link href={route} className={cn('rounded-md text-xl p-2', active && 'bg-slate-200')}>{label}</Link>
}
MalteseOP
you have to know that this solution doesn't work properly, but solution that works properly is this -
now I only have to figure out how to implement this into the /posts layout
this is the check prop on link ( acrually I think that is the worst solution to come out...Please somebody help)
MalteseOP
I came up with this and it works well for now, but I don't think if it's the best solution -
@Maltese you have to know that this solution doesn't work properly, but solution that works properly is this -
yea, actually the code above was meant to be for the sidebar
instead of the upper navigation (child of childs)
@necm1 yea, actually the code above was meant to be for the sidebar
MalteseOP
yea, I used it for the side bar