Gatsby: Add option to gatsby-link for matching active when on child routes

Created on 10 Aug 2018  Β·  10Comments  Β·  Source: gatsbyjs/gatsby

Summary

Would be great to mirror reach router's isCurrent and isPartiallyCurrent options to gatsby-links activeStyle and activeClassname props. Basically I'd want to be able to designate a Link as active when any child routes are also active, without having to bypass gatbsy-link and use isPartiallyCurrent directly.

Basic example

Hesitant to suggest just tacking on another prop as lazy DX, but that would probably make the most sense. Something like this:

<Link ... activeStyle={{...}} greedyActive={true} />

Would default to false, so non-breaking change that doesn't really add much cognitive overhead for people.

Motivation

Common use-case is top-level nav for any nested content (eg: a blog), where you'd often want to designate the nav item active (eg: 'Blog') when on a child path (eg: /blog/some-post).

If this isn't added, would at least be worth documenting how to achieve by accessing react router APIs directly in gatsby-link docs, as I imagine it's a common pattern.

Most helpful comment

Think we should keep this open, until it's clarified either way whether from the team whether this should feature be included in gatsby-link or not (I vote yes, FWIW).

Tbh I think this should be the default behaviour, with an opt-out flag instead. But that would be a breaking change so fair enough if it's added as an opt-in feature instead.

All 10 comments

:+1: as well. Until then, the following polyfill might help someone:

const isPartiallyActive = ({ isPartiallyCurrent }) =>
  isPartiallyCurrent ? { className: "item active" } : null;
<div 
  class="item"
  activeClassName={link === "/" ? "active" : undefined}
  getProps={link === "/" ? undefined : isPartiallyActive}
/>

This checks whether the link links to the root level (and thus should not be have the active class applied on child routes, but only if it is actually the current page) and if it is not, it sets className to item active.

More info is in the reach router docs. See https://gitlab.com/libresat/libresat/tree/master/packages/site for an example (blog posts).

@KyleAMathews would you accept a PR for this feature?

greedyActive should definitely be the default. In the vast majority of cases, you want child routes to "activate" their parents. I'm surprised this isn't implemented already.

@ryanflorence any thoughts here?

@KyleAMathews If @seaneking is willing to provide a PR for this feature, I think that's great. Couldn't gatsby-link implement this feature irrespective of Reach Router?

If you're worried about changing the default behavior for everyone, maybe we could add a global option to gatsby-config? Something like

module.exports = {
  siteMetadata: {
    ...
  },
  plugins: [
    ...
  ],
  settings: {
    gatsby-link: {
      greedyActive: true,
    }
  }
}

πŸ‘ as well. Until then, the following polyfill might help someone:

const isPartiallyActive = ({ isPartiallyCurrent }) =>
  isPartiallyCurrent ? { className: "item active" } : null;
<div 
  class="item"
  activeClassName={link === "/" ? "active" : undefined}
  getProps={link === "/" ? undefined : isPartiallyActive}
/>

This checks whether the link links to the root level (and thus should not be have the active class applied on child routes, but only if it is actually the current page) and if it is not, it sets className to item active.

More info is in the reach router docs. See https://gitlab.com/libresat/libresat/tree/master/packages/site for an example (blog posts).

Hi there, if at all possible would you have an example using this? I'm having problems implementing this functionality. Thanks!

@wllkle This is how I am using it.

import React from 'react'
import { Link } from 'gatsby'

const partlyActive = className => ({ isPartiallyCurrent }) => ({
  className: className + (isPartiallyCurrent ? ` active` : ``),
})

const PartlyActiveLink = ({ className, ...rest }) => (
  <Link getProps={partlyActive(className)} {...rest} />
)

In my case, the actual styles are then coming from a styled-component.

import styled from 'styled-components'

const NavLink = styled(PartlyActiveLink)`
  color: ${props => props.theme.lightBlue};
  transition: ${props => props.theme.shortTrans};
  cursor: pointer;
  &.active {
    color: ${props => props.theme.darkYellow};
  }
  :hover {
    color: ${props => props.theme.lightGreen};
  }
`

Hiya!

This issue has gone quiet. Spooky quiet. πŸ‘»

We get a lot of issues, so we currently close issues after 30 days of inactivity. It’s been at least 20 days since the last update here.

If we missed this issue or if you want to keep it open, please reply here. You can also add the label "not stale" to keep this issue open!

Thanks for being a part of the Gatsby community! πŸ’ͺπŸ’œ

Think we should keep this open, until it's clarified either way whether from the team whether this should feature be included in gatsby-link or not (I vote yes, FWIW).

Tbh I think this should be the default behaviour, with an opt-out flag instead. But that would be a breaking change so fair enough if it's added as an opt-in feature instead.

I've opened a PR, in case someone actually implementing it was all that was needed πŸ˜› (#12495)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

3CordGuy picture 3CordGuy  Β·  3Comments

timbrandin picture timbrandin  Β·  3Comments

Oppenheimer1 picture Oppenheimer1  Β·  3Comments

magicly picture magicly  Β·  3Comments

kalinchernev picture kalinchernev  Β·  3Comments