Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theming: use custom components as navbar/sidebar/footer items #7227

Open
2 tasks done
jlvandenhout opened this issue Apr 22, 2022 · 17 comments
Open
2 tasks done

Theming: use custom components as navbar/sidebar/footer items #7227

jlvandenhout opened this issue Apr 22, 2022 · 17 comments
Labels
apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future.

Comments

@jlvandenhout
Copy link

jlvandenhout commented Apr 22, 2022

Edit from @slorber:

Temporary recommended workaround

Until we have first-class support and a convenient API to support this, here's the recommended way to add custom navbar items to your site (see also #7231)

Create a file in src/theme/NavbarItem/ComponentTypes.js to add a custom navbar item type to the existing mapping:

import ComponentTypes from '@theme-original/NavbarItem/ComponentTypes';
import MyAwesomeNavbarItem from '@site/src/components/NavbarItems/MyAwesomeNavbarItem';

export default {
  ...ComponentTypes,
  'custom-myAwesomeNavbarItem': MyAwesomeNavbarItem,
};

Use it in your config:

module.exports = {
  themeConfig: {
    navbar: {
      items: [
        {
          type: 'custom-myAwesomeNavbarItem', 
          position: "left",
          itemProp: 44, 
          anotherProp: "xyz"
        },
        //... other navbar items
      ]
    }
  }
}

Note: using the custom- prefix is important: the config validation schema will only allow item types with this prefix.


Original issue

Have you read the Contributing Guidelines on issues?

Description

We'd like to propose a way to add custom navbar item components through the navbar items configuration, so they remain part of the regular layout of items in the navbar and mobile primary menu. To our understanding three things need to change:

  1. Adjust the navbar items configuration validation to allow objects with a custom type property and a custom component property, similar to how the docs plugin allows a custom docItemComponent property to override the default rendering component. This object should allow arbitrary properties, which are passed as React properties to the custom component.

  2. Adjust the NavbarItem component to allow for custom types, require their configured custom component and passing any additional custom properties to the custom component.

  3. Improve API documentation on how navbar items should distinguish between if they are rendered for mobile or desktop, so the user can properly implement the component in case such a distinction is needed.

Open questions:

  1. Do we regard any unknown type as custom type or do we require a certain pattern?
  2. Do we require the custom component property to be configured or do we not? I guess in case the user swizzles the NavbarItem component and associates the type with a custom component there, requiring the component in the navbar items configuration is not strictly necessary.

Has this been requested on Canny?

No response

Motivation

We've implemented a custom dropdown component, which currently abuses the Docusaurus provided dropdown component configuration to bypass the configuration validation. This worked until beta.17, but from beta.18 onwards our implementation breaks, because of a major refactor in the navbar, which brought us to the point where we would like to propose a properly supported way of adding custom components to the navbar.

API design

Configuration of the custom component in the navbar items configuration:

navbar: {
  items: [
    {
      type: 'custom-type',
      component: '@theme/CustomComponent',
      customProperty: ...
    },
    ...
  ],
  ...
}

Have you tried building it?

We tried swizzling the NavbarItem component, but we ran into multiple issues, including correctly typing the custom component in the type to component map and configuration validation warnings. We'd be happy to create a PR after discussing the implementation proposed above.

Self-service

  • I'd be willing to contribute this feature to Docusaurus myself.
@jlvandenhout jlvandenhout added feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future. status: needs triage This issue has not been triaged by maintainers labels Apr 22, 2022
@Josh-Cena Josh-Cena removed the status: needs triage This issue has not been triaged by maintainers label Apr 22, 2022
@Josh-Cena
Copy link
Collaborator

I mentioned on Discord we should allow type: /custom-.*/ as a pattern, because we'd still like to catch potential mistakes

@slorber
Copy link
Collaborator

slorber commented Apr 22, 2022

Hey

We definitively want to support that, and we've discussed it in a few issues already. This is not the navbar, but we'd also want custom items in other places too: doc sidebar, blog sidebar, footer...

This is an important feature requiring a bit of new infra and that we should design well, for which there are various implications (item schema validation, code-splitting, tree-shaking and lazy loading of unused custom item types, server-side-rendering, the ability for a plugin to register item types...).

As we want to launch 2.0 soon, all this is likely to be implemented later.


I mentioned on Discord we should allow type: /custom-.*/ as a pattern, because we'd still like to catch potential mistakes

Agree: in the short term the easiest workaround would be to allow pass-through of a type that starts with custom-

We should extract the component map to @theme/NavbarItem/Components or something so that it can be easily swizzled and that you can easily add your own components to this map.

And exposing a new hook like useCurrentLayout() === "mobile" can also be convenient to use in many places where we currently forward a mobile prop. (useCurrentLayout() is probably a bad name as even mobile renders both regular navbar (responsive) + mobile navbar (hamburger))

Can this be good enough for now?

@jlvandenhout
Copy link
Author

jlvandenhout commented Apr 22, 2022

we'd also want custom items in other places too

Even better!

in the short term the easiest workaround would be to allow pass-through of a type that starts with custom-
We should extract the component map to @theme/NavbarItem/Components or something so that it can be easily swizzled and that you can easily add your own components to this map.

I'll happily start working on a PR for this, I'm not strong on the schema validation logic, but I'll see how far I get. For the name of the map, what do you think of @theme/NavbarItem/ComponentTypeMap or is that too verbose?

And exposing a new hook like useCurrentLayout() === "mobile" can also be convenient to use in many places where we currently forward a mobile prop. (useCurrentLayout() is probably a bad name as even mobile renders both regular navbar (responsive) + mobile navbar (hamburger))

Looking into the codebase it looks like this is already pretty much covered by the useWindowSize hook. What do you think? Never mind, that hook won't help. We need a way to determine if we are in a mobile layout or a desktop layout, not if we are currently on mobile or desktop. So yes such a hook would be a nice addition, but how would the hook determine the local layout? What about calling it useLayoutContext()?

@slorber
Copy link
Collaborator

slorber commented Apr 22, 2022

I'll happily start working on a PR for this

I'd rather do this myself asap there are a few things that will end up becoming part of the API surface and I'd like the whole thing (including naming) to be consistent. Sometimes it's faster for me to do things directly instead of delegating and reviewing.

what do you think of @theme/NavbarItem/ComponentTypeMap

If we adopt this naming convenient we'd rather apply it as well in other places (now and future places). See for example MDXComponents

What about calling it useLayoutContext()?

That's the initial name I thought using but maybe this will be confusing considering "context" name is associated with React and we already have a Layout component 😅 As this API is likely to be adopted by power users (like you) asap, and become official later once we officially have a config api for custom navbar items, we'd rather find a good name

@slorber
Copy link
Collaborator

slorber commented Apr 22, 2022

Here's an initial version to support a custom item type: #7231

I didn't add a useLayoutContext() API for now. Considering the item receives a mobile?: boolean prop, I guess you don't really need it and would be just a convenience.

We'll keep this PR open until we have a proper API to register custom components

@slorber
Copy link
Collaborator

slorber commented Apr 28, 2022

Note that Ionic doc devs managed to implement custom navbar item types in their site, including validation of custom items: #7231 (comment)

It's a bit hacky but can also be another useful workaround to extend our classic theme

@slorber slorber changed the title Allow custom components as navbar items Theming: use custom components as navbar/sidebar/footer items Jul 22, 2022
@lukedukeus
Copy link

As far as I can tell, this feature has been merged, but I can't find any documention on how to use it. Can anyone share what works for them?

@jlvandenhout
Copy link
Author

@lukedukeus Have a look at the top post. Sebastian updated it with a recommended workaround. Hope that helps!

@lukedukeus
Copy link

Oh, that makes sense, thanks. I didn't think to try that because I assumed it was just a suggestion of how it should work. This can be closed then?

@slorber
Copy link
Collaborator

slorber commented Aug 31, 2022

We'll keep this issue open until we provide a first-class API/documentation to achieve this

Until then, the suggested workaround is good enough

@medmin
Copy link

medmin commented Nov 13, 2022

There is a bug right now.

if i use custom-navbar, somehow, in the "position:right" navbar items, if they are links, the build shows all navbar items have the last link item's href. but if you run "yarn start", all is good.

@slorber
Copy link
Collaborator

slorber commented Nov 16, 2022

@medmin it's really difficult to understand what you mean. Please add a repro, at least a screenshot of dev vs prod + a config sample.

pgrange added a commit to cardano-scaling/hydra that referenced this issue Jun 1, 2023
To prepare for versioned benchmarks and specs, we set a dropdown
for both secions.

This is close to being a dirty hack :(

We do not display the dropdown we the path does not match the
plugin Id.

Inspired by facebook/docusaurus#7227
pgrange added a commit to cardano-scaling/hydra that referenced this issue Jun 1, 2023
To prepare for versioned benchmarks and specs, we set a dropdown
for both secions.

This is close to being a dirty hack :(

We do not display the dropdown we the path does not match the
plugin Id.

Inspired by facebook/docusaurus#7227
@slorber slorber added the apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee label Sep 25, 2023
@yanni4night
Copy link

Is this feature still in plan? I want to display an custom avatar component at the most right, but it's not supported yet.

@slorber
Copy link
Collaborator

slorber commented Dec 5, 2023

Feature is still planned and one of next features we plan to work on.

The plan is to use a separate client-side file for themeConfig and let you import React components from there.

In the meantime you shouldn't be blocked, because you can swizzle.

@timothymcmackin
Copy link

Is there a workaround for a custom doc sidebar component like the custom navbar component in your workaround?

@slorber
Copy link
Collaborator

slorber commented Feb 1, 2024

Is there a workaround for a custom doc sidebar component like the custom navbar component in your workaround?

@timothymcmackin all our theme components can be swizzled so technically you can render the sidebar tree the way you want, including hardcoding your own components anywhere in that tree.

If you want to control where your custom components will render in a tree through the sidebars.js file, I can suggest using the sidebar items custom props alongside swizzling DocSidebarItem and wrapping it with extra logic:

import React from 'react';
import DocSidebarItem from '@theme-original/DocSidebarItem';

const CustomComponents = {
  'my-custom-component': (props) => (
    <div style={{ border: 'solid' }}>{JSON.stringify(props)}</div>
  ),
};

export default function DocSidebarItemWrapper(props) {
  const CustomComponent = CustomComponents[props.item?.customProps?.type];
  if (CustomComponent) {
    return <CustomComponent {...props.item?.customProps?.props} />;
  }

  return (
    <>
      <DocSidebarItem {...props} />
    </>
  );
}
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
  tutorialSidebar: [
    'intro',
    {
      type: 'doc',
      id: 'intro',
      customProps: {
        type: 'my-custom-component',
        props: { name: 'Hello World', age: 42 },
      },
    },
    {
      type: 'category',
      label: 'Tutorial',
      items: ['tutorial-basics/create-a-document'],
    },
  ],
};

export default sidebars;

CleanShot 2024-02-01 at 12 24 25

Live demo: https://stackblitz.com/edit/github-rvozvb

Yes, this is awkward to use type: 'doc' for injecting your own custom component, but at least that works and already passes validation. Note in reality you can use any sidebar item type you want, as long as the validation accepts it. In the end you'll remap the rendering of that component to your own custom component so this will be ignored.


Note: we are exploring moving themeConfig to a separate browser-based file (#9619), which would solve this problem properly. But we are facing some challenges to make it happen. Follow that issue to track progress.

@octogonz
Copy link

Temporary recommended workaround

Until we have first-class support and a convenient API to support this, here's the recommended way to add custom navbar items to your site (see also #7231)

Create a file in src/theme/NavbarItem/ComponentTypes.js to add a custom navbar item type to the existing mapping:

This was very helpful. 🙏

It would be great if this was included in the website docs somewhere, since it took some hunting to find this issue. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
apprentice Issues that are good candidates to be handled by a Docusaurus apprentice / trainee feature This is not a bug or issue with Docusausus, per se. It is a feature request for the future.
Projects
None yet
Development

No branches or pull requests

8 participants