Next.js Discord

Discord Forum

Problem with styling nested routes

Answered
Maltese posted this in #help-forum
Open in Discord
Avatar
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
View full answer

54 Replies

Avatar
MalteseOP
Image
Avatar
MalteseOP
Image
Avatar
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(...)
Avatar
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
Avatar
So, I've some suggestions including the fix for your issue.
Avatar
then you can still find the most matching link and the most matching then will be highlighted
Avatar
MalteseOP
you are talking about making the paths absolute?
Image
this works, yea, but I want to know how I can make it with relative paths
Avatar
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;
}
Avatar
MalteseOP
pfff that looks a little complicated haha 😂
is this the best option?
Avatar
that's a solution how you find the most matching link. Hopefully it looks not too complicated.. I translated it from another (java 💀 ) code
Image
Avatar
MalteseOP
☠️
Avatar
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:
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
Avatar
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
Avatar
now in your NavSideBar component

{sidebarLinks.map((link, index) => (<NavSideBarItem key={`${link.label}-${index}`} {...link} />))}
Avatar
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
Avatar
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
Avatar
take a look on the example in the docs
Image
// Navigating to /blog/hello-world will return 'hello-world'
Avatar
and navigating to /block/hello-world/hello would also return hello-world, right?
Avatar
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
Avatar
a bit confusing... but somehow it also makes sense 🤔
cool hook. Never heard about it
Image
Avatar
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
would suggest to remove the domain inside the route path:

{
  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
btw. if it worked for you, could you maybe mark my answer as solution described here:
Avatar
MalteseOP
yea, I will try it soon
Avatar
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
Image
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
Avatar
oh
didn't see these on top
you could maybe just write again a new TopNavbarItem
and then use LayoutSegemnts
Avatar
MalteseOP
you have to know that this solution doesn't work properly, but solution that works properly is this -
Image
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)
Image
Avatar
MalteseOP
I came up with this and it works well for now, but I don't think if it's the best solution -
Image
Avatar
yea, actually the code above was meant to be for the sidebar
instead of the upper navigation (child of childs)
Avatar
MalteseOP
yea, I used it for the side bar