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

[useRender] Add public hook #1418

Open
wants to merge 58 commits into
base: master
Choose a base branch
from
Open

Conversation

mnajdova
Copy link
Member

@mnajdova mnajdova commented Feb 5, 2025

Closes #1315

Added a public useRender hook as an simpler adapter that uses the useComponentRenderer hook. I intentionally copied the types, so we make sure we don't break them if we ever change the internal useComponentRenderer hook.

The hooks receives the following settings:

  • className
  • render
  • state
  • props (passed as extraProps)
  • stateAttributesMap (alias for customStyleHookMapping)

Documentation page: https://deploy-preview-1418--base-ui.netlify.app/react/utils/use-render

Copy link

netlify bot commented Feb 5, 2025

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit 3340a4e
🔍 Latest deploy log https://app.netlify.com/sites/base-ui/deploys/67bde59c58d2840008cff0de
😎 Deploy Preview https://deploy-preview-1418--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@mj12albert mj12albert linked an issue Feb 5, 2025 that may be closed by this pull request
@mnajdova mnajdova marked this pull request as draft February 5, 2025 11:56
@mnajdova mnajdova added the new feature New feature or request label Feb 5, 2025
Copy link
Member

@mj12albert mj12albert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice ~ I imagine the use-case is something like this: https://codesandbox.io/p/sandbox/flamboyant-fog-9x266p?file=%2Fsrc%2FApp.tsx%3A29%2C63
(for some reason importing the hook in CSB doesn't work, this works fine locally in a playground though)

This feels more intuitive to me (biased though because I never used asChild extensively) to use than a Slot component

@mnajdova
Copy link
Member Author

mnajdova commented Feb 5, 2025

for some reason importing the hook in CSB doesn't work, this works fine locally in a playground though

I haven't updated the exports field, will fix it. Here is the updated sandbox: https://codesandbox.io/p/sandbox/exciting-paper-9x266p-use-renderer-forked-ry269y?file=%2Fpackage.json

@mj12albert
Copy link
Member

I removed only the customStyleHookMapping setting from the original hook

It may be useful to provide a (alternative) way to opt-out individual props placed in state from generating a corresponding data attribute

@mnajdova
Copy link
Member Author

mnajdova commented Feb 6, 2025

Thanks for the initial review, I updated the API to include:

@michaldudak
Copy link
Member

I'd leave customStyleHookMapping as in useComponentRenderer. It should be quite common to customize the data attributes (we do this often).

@mnajdova
Copy link
Member Author

mnajdova commented Feb 6, 2025

I'd leave customStyleHookMapping as in useComponentRenderer. It should be quite common to customize the data attributes (we do this often).

Yeah, fair enough, ok I was thinking initially to make the API simpler. I've changed it back to use the customStyleHookMapping and added a test for it.

@mnajdova mnajdova marked this pull request as ready for review February 7, 2025 08:45
@mnajdova
Copy link
Member Author

mnajdova commented Feb 7, 2025

I've resolved all feedback and added a docs page: https://deploy-preview-1418--base-ui.netlify.app/react/utils/use-renderer

@mnajdova mnajdova requested a review from michaldudak February 7, 2025 09:33
@mnajdova mnajdova changed the title [useRenderer] Add public hook [useRenderer] Add public hook & Slot component Feb 12, 2025
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Feb 24, 2025
@@ -26,4 +26,5 @@ export * from './tabs';
export * from './toggle';
export * from './toggle-group';
export * from './tooltip';
export * from './use-render';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why useRender is not inside /utils?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michaldudak @atomiks what are your thoughts? I though it as a "first class export" similar to the components, but having it in the utils makes sense as well, especially if we want to publicly make available more of the hooks.

@aarongarciah
Copy link
Member

aarongarciah commented Feb 24, 2025

Docs updated. Highlights:

  • The basic render prop demo has been simplified.
  • The data-attrs and className demos are now two separate demos, for better discoverability. The end result in both demos is the same, but achieved differently.
  • The data-attrs and className use a Counter button instead of a clickable Text component. Not my favorite example but I think it's better than the previous one.
  • New "Migrating from Radix" section added.
  • Other copy updates e.g. replacing the "Usage" headline with "Examples", which we use in all components.

I'm still not happy with the subtitle ("Utility for adding Base UI-like features to custom components") and the intro bullet points below. I think 99% os users will use useRender only to implement their own render prop, and the state-based data-attrs and className callback are only nice to have features for a minority of users i.e. only useful for users building unstyled primitives, which are not very numerous? The subtitle and bullet points probably makes useRender look more complex that it should because of this.

One of the reasons why it's so hard to come up with simple examples for the data-attrs and className callback demos is because it's mostly useful for unstyled primitives, and unstyled primitives already exist on Base UI, and tend to be complex (or at least require a lot of boilerplate to be fully accessible).

Note: state-based data attributes can be useful for styled components that want to have self-contained styles based on data attributes (.Text[data-size="large"]), but IMO that doesn't justify such a feature in useRender.

After having though about useRender for a while, shipping useRender without data-attrs and className callback could be an option. I'm curious to know your thoughts cc @mnajdova @michaldudak @colmtuite

@mj12albert
Copy link
Member

I'm still not happy with the subtitle ("Utility for adding Base UI-like features to custom components")

How about "Utility for creating custom components that support a render prop"?

@michaldudak
Copy link
Member

After having though about useRender for a while, shipping useRender without data-attrs and className callback could be an option.

It's not a bad idea, especially considering that state attributes and class names implementation is pretty simple and should be easily done in userland if needed (or we could provide separate utilities for them if devs want them).

'The state of the component. It will be used as a parameter for the render and className callbacks.',
},
stateAttributesMap: {
type: 'StateAttributes<State>',
Copy link
Member

@aarongarciah aarongarciah Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK StateAttributes is not exposed to users so not sure if having it here is helpful.

@mnajdova
Copy link
Member Author

After having though about useRender for a while, shipping useRender without data-attrs and className callback could be an option.

It's not a bad idea, especially considering that state attributes and class names implementation is pretty simple and should be easily done in userland if needed (or we could provide separate utilities for them if devs want them).

Yeah, we can do this in the first iteration. @aarongarciah I will do the change on the hook & docs, if we decide to extend it in the future, we can use your examples.

@mnajdova
Copy link
Member Author

I've simplified the hook & the docs page to only cover the render prop, we can add more utils in the future if people ask for them.


return useComponentRenderer({
render,
state: {} as Record<string, any>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

off-topic: I wonder if state should be optional in useComponentRenderer

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably didn't have use-case for it so far, as all components have some state, but we could consider it if we hit this need again.

mnajdova and others added 4 commits February 25, 2025 16:45
Co-authored-by: Aarón García Hervás <[email protected]>
Signed-off-by: Marija Najdova <[email protected]>
Co-authored-by: Aarón García Hervás <[email protected]>
Signed-off-by: Marija Najdova <[email protected]>
Co-authored-by: Aarón García Hervás <[email protected]>
Signed-off-by: Marija Najdova <[email protected]>
Co-authored-by: Aarón García Hervás <[email protected]>
Signed-off-by: Marija Najdova <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Slot component vs useRender hook [core] Could useComponentRenderer be exported as a util?
6 participants