skeleton icon indicating copy to clipboard operation
skeleton copied to clipboard

NEXT: Review options for sharing and maintaining common component data between frameworks

Open endigo9740 opened this issue 1 year ago • 2 comments

Following our recent integration of Zag.js, we're now unifying logic and state handling for most components and features using Zag itself. However, there's still a lot of repetition in how we scaffold components per each framework, including:

  1. Common prop names
  2. Common default prop values
  3. Common types and interfaces
  4. Common JSDoc comments for type definitions

We should investigate potential solutions to provide a single source of truth for this information.

Potential Issues

Hugo and I have discussed this on several occasions, but the biggest barrier of entry is that not all components are constructed symmetrically per framework. For simple components such as Avatars the implementation is quite close. The same list of props, values, types, etc:

However, this is not always the case. Select components, such as Accordions and Tabs, can differ wildly:

The most common reason for this being React (or JSX-based component systems) lacking an 1:1 equivalent for named slots. For example:

Svelte:

<Tabs bind:value={group}>
	{#snippet list()}
		<Tabs.Control value="plane">Plane</Tabs.Control>
		<Tabs.Control value="boat">Boat</Tabs.Control>
		<Tabs.Control value="car">Car</Tabs.Control>
	{/snippet}
	{#snippet content()}
		<Tabs.Panel value="plane">Plane Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="boat">Boat Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="car">Car Panel - {lorem}</Tabs.Panel>
	{/snippet}
</Tabs>

React:

<Tabs value={group} onValueChange={setGroup}>
	<Tabs.List>
		<Tabs.Control value="plane">Plane</Tabs.Control>
		<Tabs.Control value="boat">Boat</Tabs.Control>
		<Tabs.Control value="car">Car</Tabs.Control>
	</Tabs.List>
	<Tabs.Content>
		<Tabs.Panel value="plane">Plane Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="boat">Boat Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="car">Car Panel - {lorem}</Tabs.Panel>
	</Tabs.Content>
</Tabs>

While fairly similar from the end user's perspective, there's some notable differences:

  • Svelte: uses snippets for List/Control; requires only 3 components (Root, List, Panel)
  • React: requires dedicated components for each part of the component tree structure

Note that React does supply React.Node props, allowing you to pass template data through props. But the user-facing API for doing this is not widely used in any framework we've reviewed. And personally speaking is quite unfriendly:

<Tabs
	value={group}
	onValueChange={setGroup}
	list=(
		<Tabs.Control value="plane">Plane</Tabs.Control>
		<Tabs.Control value="boat">Boat</Tabs.Control>
		<Tabs.Control value="car">Car</Tabs.Control>
	)
	content=(
		<Tabs.Panel value="plane">Plane Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="boat">Boat Panel - {lorem}</Tabs.Panel>
		<Tabs.Panel value="car">Car Panel - {lorem}</Tabs.Panel>
	)
/>

This is great for small additions, such as icons. But would quickly become unmanageable once the size of the child content reached a certain length.

Potential Solutions

So far we do not have a solid solution for this scenario. However, the following ideas have been discussed:

  1. Standardize all components around the composed tree of components (ala React), unfortunately this hurts the DX for Svelte, Vue and other frameworks on both the end user and maintainer/contributor side of things. We don't get to use the handy tools they provide (Snippets, slots, etc)
  2. Keep separate structures, but find an alternative way to define the non-uniform props/types per these components. Unfortunately this returns us to the original issue we're trying to solve.

We welcome additional ideas and proposals around this in the comments section below!

endigo9740 avatar Aug 13 '24 18:08 endigo9740

As much as I'd like to revisit this, I think is going to have to be bumped to to a major update post-v3. Perhaps when we begin implementing our next component library?

endigo9740 avatar Dec 16 '24 22:12 endigo9740

Just wanted to say this goes hand in hand with my component worries, shared prop names would be possible.

Hugos68 avatar Mar 06 '25 08:03 Hugos68

I'm going to go ahead and close this out, as we have now established /packages/skeleton-common for this purpose in Skeleton v4 and forward.

endigo9740 avatar Sep 23 '25 16:09 endigo9740