You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In real-world applications, navigation menus often need to handle complex highlighting logic to indicate the currently active link. Solid Router provides the <A> component with an activeClass option and built-in logic to apply the active class. However, the current functionality has limitations when managing nested routes and ensuring that only one link is highlighted at a time.
This discussion aims to address these limitations by proposing enhancements to Solid Router or exporting useful primitives that simplify custom implementations for advanced use cases.
The Problem
A common use case involves a navigation menu like this:
Navigating to /profile/subscriptions results in both 'Profile' and 'Subscriptions' being highlighted. Adding end={true} to the 'Profile' link solves this for /profile, but causes other issues, such as no link being highlighted for /profile/security or /profile/subscriptions/invoices.
The desired behavior is to always highlight exactly one link, as shown in this table:
location.pathname
Highlighted Link
/
Home
/profile
Profile
/profile/security
Profile
/profile/subscriptions
Subscriptions
/profile/subscriptions/invoices
Subscriptions
Existing Workaround
To achieve this behavior, I examined the source code of Solid Router's <A> component, particularly the isActive accessor, and created a custom hook:
Building on this, I implemented a custom hook, createMatchSelector, to determine the best-matching link. However, this approach requires accessing Solid Router internals like normalizePath and duplicating parts of the isActive logic, which feels brittle and error-prone.
Here is the implementation of the hook:
typeNavLink={href: string;end?: boolean;// true: exact match; false: prefix match.};/** * Creates a selector to match the single best-matching link from the provided `links` collection. * Useful for rendering a navigation menu where only one link is marked as active. * @param links A list of links to determine the best match. * @returns A selector that returns `true` for the best-matching link in the `links` collection. */functioncreateMatchSelector(links: Accessor<NavLink[]>): (key: string|undefined)=>boolean{constbestMatch=selectBestMatch(links);constactive=createSelector<NavLink|undefined,string|undefined>(bestMatch,(a,b)=>!!a&&!!b&&a===b?.href);returnactive;}/** * Selects the single best-matching link from the provided `links` collection as a cached computation. * @param links A collection of links. * @param location The URL pathname to match against. Defaults to `location.pathname`. * @returns The best-matching NavLink or undefined if no match is found. */exportfunctionselectBestMatch(links: Accessor<NavLink[]>,location?: Accessor<string>): Accessor<NavLink|undefined>{const_location=useLocation();returncreateMemo(()=>{constmatcher=createPathMatcher(location??(()=>_location.pathname));// Select the best match by choosing the longest link from all matching links.constmatch=links().filter(matcher).sort((a,b)=>(a.href.length>b.href.length ? -1 : 1))[0];returnmatch;});}/** * Creates a matcher function to determine if a given `NavLink` matches the current `pathname`. * @param pathname The URL pathname to match against. * @returns A matcher function that returns true if the provided NavLink matches the current `pathname`. */exportfunctioncreatePathMatcher(pathname: Accessor<string>){constloc=decodeURI(/*normalizePath*/(pathname()).toLowerCase());return(link: NavLink)=>{// Logic originated from `isActive` in <A> component:constto=link.href;if(to===undefined)returnfalse;constpath=/*normalizePath*/(to.split(/[?#]/,1)[0]).toLowerCase();returnlink.end
? path===loc
: loc.startsWith(path+"/")||loc===path;};}
Usage Example
The createMatchSelector hook can be used in a component like this to manage a tabbed navigation menu with consistent highlighting for the active link:
This example demonstrates how to apply the createMatchSelector hook to consistently highlight a single active link in a navigation menu.
Proposed Solutions
Add createMatchSelector to Solid Router
This would make it straightforward to implement navigation menus with consistent single-link highlighting logic.
Export Useful Primitives
Refactoring <A> component and exporting utilities like selectBestMatch and createPathMatcher would enable developers to build such custom hooks more robustly.
Conclusion
Solid Router is a fantastic tool, and extending its capabilities with these enhancements would address common navigation challenges while maintaining flexibility for developers.
I'm open to feedback, suggestions, and alternative approaches to solve this issue more elegantly. Let me know your thoughts! 😊
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Introduction
In real-world applications, navigation menus often need to handle complex highlighting logic to indicate the currently active link. Solid Router provides the
<A>
component with anactiveClass
option and built-in logic to apply the active class. However, the current functionality has limitations when managing nested routes and ensuring that only one link is highlighted at a time.This discussion aims to address these limitations by proposing enhancements to Solid Router or exporting useful primitives that simplify custom implementations for advanced use cases.
The Problem
A common use case involves a navigation menu like this:
With routes defined as follows:
Navigating to
/profile/subscriptions
results in both 'Profile' and 'Subscriptions' being highlighted. Addingend={true}
to the 'Profile' link solves this for/profile
, but causes other issues, such as no link being highlighted for/profile/security
or/profile/subscriptions/invoices
.The desired behavior is to always highlight exactly one link, as shown in this table:
location.pathname
/
/profile
/profile/security
/profile/subscriptions
/profile/subscriptions/invoices
Existing Workaround
To achieve this behavior, I examined the source code of Solid Router's
<A>
component, particularly theisActive
accessor, and created a custom hook:solid-router/src/components.tsx
Lines 50 to 56 in 50c5d7b
Building on this, I implemented a custom hook,
createMatchSelector
, to determine the best-matching link. However, this approach requires accessing Solid Router internals likenormalizePath
and duplicating parts of theisActive
logic, which feels brittle and error-prone.Here is the implementation of the hook:
Usage Example
The
createMatchSelector
hook can be used in a component like this to manage a tabbed navigation menu with consistent highlighting for the active link:This example demonstrates how to apply the
createMatchSelector
hook to consistently highlight a single active link in a navigation menu.Proposed Solutions
Add
createMatchSelector
to Solid RouterThis would make it straightforward to implement navigation menus with consistent single-link highlighting logic.
Export Useful Primitives
Refactoring
<A>
component and exporting utilities likeselectBestMatch
andcreatePathMatcher
would enable developers to build such custom hooks more robustly.Conclusion
Solid Router is a fantastic tool, and extending its capabilities with these enhancements would address common navigation challenges while maintaining flexibility for developers.
I'm open to feedback, suggestions, and alternative approaches to solve this issue more elegantly. Let me know your thoughts! 😊
Beta Was this translation helpful? Give feedback.
All reactions