qwik polymorphism

Tobias Zimmermann2 Minuten Lesezeit

Even in Qwik you sometimes need more complex components that need to be rendered both as a link and as a button or whatever element.

QwikIntrinsicElements#

The type QwikIntrinsicElements is the base you should rely on here. The Qwik team has extended the HTMLAttributes here.

What can your props look like now? As the title suggests, we can't get around generics.

For my button component, the props look like this:

tsx
export type ButtonProps<C extends keyof QwikIntrinsicElements> =
QwikIntrinsicElements[C] & {
as?: C;
};

C is then the prop that decide what props are used from QwikIntrinsicElements.

The implementation for this is then altogether the following:

tsx
import type { QwikIntrinsicElements } from '@builder.io/qwik';
import { component$, Slot } from '@builder.io/qwik';
export default component$(
<C extends keyof QwikIntrinsicElements>(props: ButtonProps<C>) => {
const Component = (props.as ?? 'button') as string;
return (
<Component>
<Slot />
</Component>
);
}
);
export type ButtonProps<C extends keyof QwikIntrinsicElements> =
QwikIntrinsicElements[C] & {
as?: C;
};

You are welcome to add your own props under as?.

Casting as string#

tsx
const Component = (props.as ?? 'button') as string;

Unfortunately there seems to be no clean carry over for the key into JSX yet. I have experimented quite a bit and worked with keyof QwikIntrinsicElements etc. I had first worked with any, but since I'm sure many of you use Prettier and Lint, I find string a bit more appropriate here.

Much easier than in React, where you always have to think forwardRef as well.

Have fun with Polymorphism