Closed thejackshelton closed 5 months ago
Hey,
we discussed this notation while creating the Modal component.
Back that time TreeShaking was the main reason why we switched from Dot-notation to the current version.
Another argument was that Dot-notation might make the developer that the provided components cannot be replaced with own implementations.
Nevertheless Dot-notation is easier to understand. You get auto-completion. There is one single API entry-point.
Question
I am a big fan of the Dot-notation.
I think the dot notation makes discovery much better. I really like the idea of supporting both syntaxes so more advanced users can only import the components they need for the most finely tuned bundle.
Hey,
we discussed this notation while creating the Modal component.
Back that time TreeShaking was the main reason why we switched from Dot-notation to the current version.
Another argument was that Dot-notation might make the developer that the provided components cannot be replaced with own implementations.
Nevertheless Dot-notation is easier to understand. You get auto-completion. There is one single API entry-point.
Question
- Is Tree-shaking really a problem
- From my understanding more is can end up in the compiled bundle, but does this mean it is also loaded by the client? In Qwik we have these cool Code-Splitting mechanisms.... Maybe there is more in the bundle, but does this mean that all this code is also loaded? π€
I am a big fan of the Dot-notation.
Yeah, so I don't think it's a problem for us. I tested via a new Qwik project, and it seemed to tree shake out each of the "parts" just great.
Qwik's code splitting seems to do the trick here. It also did not execute those parts until interaction.
I think the dot notation makes discovery much better. I really like the idea of supporting both syntaxes so more advanced users can only import the components they need for the most finely tuned bundle.
Yeah it wouldn't be a breaking change at that point either. I guess the tradeoff here would be double the exports π¬ . Not sure if we'd run into any bundle problems there or not.
But @cwoolum I believe both solutions give you a fine-grained bundle since it properly tree shakes each component. We are exporting each exact piece for example:
export { SelectRoot as Root } from './select'
and then export * as Select from './components/select'
The current export is:
in the component
export * from './select'
in the lib
export * from './components/select'
Edit: added one pro from @wmertens on Discord
Here are my thoughts π
Pros:
import { Collapsible } from '@qwik-ui/headless';
read nicely and this will make the styled-components copy/paste code MUCH MUCH cleanerCons:
Neutral:
<Modal.Description />
for headless and <ModalDescription />
for styled.. But I'm thinking <Modal.Description />
for styled could be cool as well for the autocomplete π
.. So yeah neutral..<Modal.Description />
reads the same as <ModalDescription />
to me.<Modal.Description />
. I also have autocomplete with <ModalDescription />
(when I start writing Modal, vscode suggests me all the headless modal components) -> So kind of neutral too..Conclusion:
It's two small pros for a rather small con (since breaking change at this point shouldn't be that big of a deal), but what's missing in my analysis is the feeling of the dot syntax. I believe copy/pasting a styled component that imports a headless component with the dot syntax will be slightly less scary and feel cleaner overall. Also better namespacing means easier maintenance. So I would lean towards dot syntax.
Bonus: This article explains the tradeoffs well https://medium.com/@skovy/using-component-dot-notation-with-typescript-to-create-a-set-of-components-b0b2aad4892b
Also, you export all the helper components a component uses, even if that is duplication. That's easier for discovery and costs nothing.
I think in this case where main and helper components are so tightly coupled, this is a great syntax. π
My main concern with the namespace is component reusability. For example you might want to have the same Option
component for Listbox
, Combobox
, Select
, ...
Should the user prefix the Option
with the current namespace?
Is it possible to reexport the Option
as part of each namespace without increasing the bundle size ?
@GrandSchtroumpf my gut tells me that it reads more nicely to have Listbox.Trigger
, Combobox.Trigger
, Select.Trigger
even though it's the same Trigger
component under the hood for all of them. The advantage is that you can instantly know visually that you're dealing with a Listbox sub-component and not a custom component of yours.
My main concern with the namespace is component reusability. For example you might want to have the same
Option
component forListbox
,Combobox
,Select
, ... Should the user prefix theOption
with the current namespace? Is it possible to reexport theOption
as part of each namespace without increasing the bundle size ?
Hm... if it's intended to be a reusable component, I don't believe it would be a namespaced component to begin with. For example, VisuallyHidden
is going to remain a regular component.
The tricky part with something like a reusable listbox or option component is context.
However, what you are saying is how I believe React Aria does it. For example:
<Select>
<Label>Favorite Animal</Label>
<Button>
<SelectValue />
<span aria-hidden="true">βΌ</span>
</Button>
<Popover>
<ListBox>
<ListBoxItem>Aardvark</ListBoxItem>
<ListBoxItem>Cat</ListBoxItem>
<ListBoxItem>Dog</ListBoxItem>
<ListBoxItem>Kangaroo</ListBoxItem>
<ListBoxItem>Panda</ListBoxItem>
<ListBoxItem>Snake</ListBoxItem>
</ListBox>
</Popover>
</Select>
This is their select component API.
Every single component is able to be reusable. I believe their value proposition though is more tailored towards using their hooks, and then their own state management library, but I am still unsure how they are able to reuse everything π , and what the tradeoffs of that would be.
And maybe that's the direction we should go! I am still unsure of that. We have a Qwik UI meeting tomorrow if you'd like to help us take a stab at a much more reusable approach.
How many components are already using the dot notation vs the non-dot-notation ? If there's 10% dot notation - would it make sense to undertake such an enormous piece of work? And vice versa - if only 10% are not - make them all dot notation...
How many components are already using the dot notation vs the non-dot-notation ? If there's 10% dot notation - would it make sense to undertake such an enormous piece of work? And vice versa - if only 10% are not - make them all dot notation...
There's some components where it would be odd to do so, like <Label />
. Dot notation works really great when you have "pieces" or sub-components.
As for what you mentioned first, the library is already moved to dot notation, we will be releasing 0.4 once the styled kit has fully moved over. π¨
Thanks everyone who participated! π implemented in 0.4
Summary
Most headless ui libraries with a component based approach will use a dot notation component, also known as a namespaced component.
The current Qwik UI API does not use namespaced components. This RFC intends to discuss whether we should:
Example
Our current syntax is something like:
Motivation
Namespaced components offer a couple advantages:
<Modal.Description />
is easier to follow than<ModalDescription />
Drawbacks
It would appear that this syntax does in-fact properly tree shake. It also does not wake up the framework or any other shenanigans from first glance.
here is a demo of the behavior: https://github.com/thejackshelton/qwikui-select-city
We would have to change the API of each component to meet this new convention. This means a potential breaking change. Because Qwik UI is still pre 1.0, an API change here could be warranted.
Migration Strategy
A potential migration strategy could be exporting both our current syntax, and the new one:
Then the consumer could choose to use either
<Select />
or<Select.Root />
I'm not sure this is worth the effort, as it leads to potential tree shaking issues.
FAQs
So how do you get a namespaced component?
Having a lower level barrel file, for example:
in the component file
in the library export
This becomes: