Skip to content

Conversation

@michaelhazan
Copy link

@michaelhazan michaelhazan commented Nov 7, 2025

Closes #3142.
Closes #3756.

Note

Originally this was meant to only introduce generic types to the ToggleGroup, but i realized while working on this that Toggle too had to be changed to handle a generic value.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 7, 2025

commit: cf9b46e

@mui-bot
Copy link

mui-bot commented Nov 7, 2025

Bundle size report

Bundle Parsed size Gzip size
@base-ui/react 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch from 787028e to 84ff83c Compare November 7, 2025 23:28
@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch from 84ff83c to b5ae7e4 Compare November 7, 2025 23:32
@netlify
Copy link

netlify bot commented Nov 7, 2025

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit cf9b46e
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/69793f0a4026b7000809752d
😎 Deploy Preview https://deploy-preview-3173--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 project configuration.

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Nov 7, 2025
Signed-off-by: Michael Hazan <michaelhazan@hilma.tech>
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Nov 7, 2025
@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch from f39e177 to 81f77e4 Compare November 8, 2025 11:02
@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch from 81f77e4 to a3ea128 Compare November 8, 2025 11:04
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Nov 10, 2025
Signed-off-by: Michael Hazan <michaelhazan@hilma.tech>
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Nov 10, 2025
@LukasTy LukasTy added type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. component: toggle Changes related to the toggle component. typescript component: toggle group Changes related to the toggle group component. labels Nov 10, 2025
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Dec 12, 2025
@michaelhazan
Copy link
Author

@mj12albert sorry it took me so long, work had me backed up 😅

it should be good now, because Toggle now extends BaseUIComponentProps, i had to make the value an extendable of number | string, should nonetheless be an improvement to use with Enums and Constant Objects :D

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jan 16, 2026
Copy link
Member

@LukasTy LukasTy left a comment

Choose a reason for hiding this comment

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

@michaldudak, is this still relevant after merging #3770?

@michaldudak
Copy link
Member

michaldudak commented Jan 21, 2026

@LukasTy, yes, I haven't noticed this PR. It's basically an equivalent of what I've recently done in #3788.

This approach has a small issue we discussed internally:

When Toggle's value is undefined, we generate it using useBaseUiId, which returns a string. So we can't really guarantee that ToggleGroup<T> with Toggle<T> components inside is indeed type-safe. Options to solve it could be:

  • Enforce a Toggle's value when it's used within a ToggleGroup. It actually doesn't make much sense otherwise, but technically is a breaking change.
  • Ignore it. Since the contract between Toggle and ToggleGroup is weak anyway (we can't enforce them to use the same type of value), it will be up to developers to make sure the correct values are provided. I think it should be possible to make it a bit more obvious by requiring value on a type level whenever a type parameter is set explicitly (so <Toggle<'left' | 'right'> would error in TS without a value)

Also, I'm afraid we can't use | number at this point, as it would break all the codebases that assume it's a string.

@LukasTy
Copy link
Member

LukasTy commented Jan 21, 2026

@michaldudak Thanks for sharing more details. 🙏
Do you think it makes sense to keep this PR open for v2 as a breaking change, depending on which route we decide to go with (enforce a value or consider supporting | number as well if there is enough need for it)?

@michaldudak
Copy link
Member

We discussed this with the team and agreed that we can proceed with this implementation ignoring the potentially incorrect types. To further prevent surprises, we can add a dev-time warning when a Toggle within a ToggleGroup doesn't have an explicit value.

@michaelhazan, would you like to continue working on this?

@michaelhazan
Copy link
Author

@michaelhazan, would you like to continue working on this?

Sure!
I will merge it with main and add the warning 👍

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Jan 24, 2026
@michaelhazan
Copy link
Author

@michaldudak Hey! should be good now..
if you have anymore suggestions you want me to implement let me know!

Copy link
Member

@LukasTy LukasTy left a comment

Choose a reason for hiding this comment

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

Thank you for your effort. 🙏
Leaving a few suggestions and one discussion point.

export namespace ToggleGroup {
export type State = ToggleGroupState;
export type Props = ToggleGroupProps;
export type Props<Value extends string> = ToggleGroupProps<Value>;
Copy link
Member

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but is there a use case for why we still need the generic here? 🤔

As far as I can see, it only helps with type inference, like if you have the following:

type Alignment = 'horizontal' | 'vertical';

const alignments: Alignment[] = ['horizontal', 'vertical'];

<ToggleGroup
  multiple
  defaultValue={alignments}
  // this is going to know that the type is `Alignment` instead of plain string as it is now
  value={}
>

But, the current types don't seem to block the above usage as well.

If we go with this, then we should probably adjust other components (i.e., RadioGroup, CheckboxGroup) accordingly.

WDYT @michaldudak?

Copy link
Member

Choose a reason for hiding this comment

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

I think this is useful and addresses #3756

(it should also be able to infer the value arg in onValueChange I assume)

Copy link
Member

@LukasTy LukasTy Jan 27, 2026

Choose a reason for hiding this comment

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

Gotcha. Makes sense, especially given we already have others noticing this shortcoming. 👍
I think we need to do a follow-up and address this issue for all relevant components.

Copy link
Author

Choose a reason for hiding this comment

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

@LukasTy would you like me to fix it in this pr? or leave it for now?

Copy link
Member

Choose a reason for hiding this comment

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

I think we can keep this one isolated to these specific components.
WDYT?

Copy link
Author

@michaelhazan michaelhazan Jan 27, 2026

Choose a reason for hiding this comment

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

for sure

@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch 2 times, most recently from 0780e10 to 9cdae80 Compare January 27, 2026 22:40
…o check for it

also made the generic type in `ToggleGroup` & `Toggle` default to `string` to match the behavior in `SliderRootProps`.
@michaelhazan michaelhazan force-pushed the toggle-group-type-safety branch from 9cdae80 to cf9b46e Compare January 27, 2026 22:41
@michaelhazan
Copy link
Author

michaelhazan commented Jan 27, 2026

@LukasTy fixed!
was uncertain if you wanted me to implement something from your comment here, from what i got, you want to make a general change in the library following what i've done here and #3756 , and therefore no change in necessary in this pr.

did i get that right? 😅

please note i've also set the props's generic type to equal string by default after i saw similar thing in SliderRootProps.
here and here

@michaelhazan michaelhazan requested a review from LukasTy January 27, 2026 22:53
useIsoLayoutEffect(() => {
if (groupContext && valueProp === undefined) {
warn(
'A `<Toggle>` component rendered in a `<ToggleGroup>` has no explicit `value` prop. This can cause type issues between the Toggle Group and Toggle values. Provide the `<Toggle>` with a `value` prop matching the `<ToggleGroup>` values prop type.',
Copy link
Member

Choose a reason for hiding this comment

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

What type issue does this cause?
This seems like a clumsy but legit way to use toggle group without values:

<ToggleGroup>
  <Toggle onPressedChange={setState1} />
  <Toggle onPressedChange={setState2} />
</ToggleGroup>

Copy link
Member

Choose a reason for hiding this comment

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

I think Michal raised this point that in case the <ToggleGroup> defaultValue would have a type of Alignment[], then you'd end up in a slight type discrepancy, because internally a Toggle without a value would have generated an internal value type of string.
However, this is a very edge case condition. 🤔
Would it make more sense if the warning would be shown only in your case (no value on the Toggle and no default and no direct value on the Toggle Group)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: toggle group Changes related to the toggle group component. component: toggle Changes related to the toggle component. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[toggle group] any[] value type breaks type safety [ToggleGroup] Better type safety

5 participants