S
SolidJSā€¢2mo ago
-=Camille=-

Component that can render a different tag depending on prop?

I want to make a title that renders a different component depending on the as props you give it. I've written the following component, using a Switch to render the correct component. I am not sure if this is the best way to accomplish what I want. What are your opinions on the way I should have done this? Is this good practice, or is there a cleaner way? Thank you very much for your help and your time! šŸ™
import { JSXElement, Match, Switch } from 'solid-js';

import { TextColour } from './types/TextColour';

type TitleProps = {
/**
* The heading level of the title. Defaults to 'h1'.
*/
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
colour?: TextColour;
children: JSXElement;
variant?: 's' | 'm' | 'l' | 'xl';
};

/**
* Renders a title component based on the specified heading level.
* Defaults to 'h1' heading level, 'black' colour, and 'm' variant size.
*
* @example
* <Title as="h2" colour="dark-brown" variant="l">My Title</Title>
*/
const Title = (props: TitleProps) => {
const getClasslist = () => {
const classes: Record<string, boolean> = {};
classes[`title--${props.variant ?? 'm'}`] = true;
classes[`title--${props.colour ?? TextColour.BLACK}`] = true;
return classes;
};

return (
<Switch fallback={<h1 classList={getClasslist()}>{props.children}</h1>}>
<Match when={props.as === 'h2'}>
<h2 classList={getClasslist()}>{props.children}</h2>
</Match>
<Match when={props.as === 'h3'}>
<h3 classList={getClasslist()}>{props.children}</h3>
</Match>
<Match when={props.as === 'h4'}>
<h4 classList={getClasslist()}>{props.children}</h4>
</Match>
<Match when={props.as === 'h5'}>
<h5 classList={getClasslist()}>{props.children}</h5>
</Match>
<Match when={props.as === 'h6'}>
<h6 classList={getClasslist()}>{props.children}</h6>
</Match>
</Switch>
);
};

export default Title;
import { JSXElement, Match, Switch } from 'solid-js';

import { TextColour } from './types/TextColour';

type TitleProps = {
/**
* The heading level of the title. Defaults to 'h1'.
*/
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
colour?: TextColour;
children: JSXElement;
variant?: 's' | 'm' | 'l' | 'xl';
};

/**
* Renders a title component based on the specified heading level.
* Defaults to 'h1' heading level, 'black' colour, and 'm' variant size.
*
* @example
* <Title as="h2" colour="dark-brown" variant="l">My Title</Title>
*/
const Title = (props: TitleProps) => {
const getClasslist = () => {
const classes: Record<string, boolean> = {};
classes[`title--${props.variant ?? 'm'}`] = true;
classes[`title--${props.colour ?? TextColour.BLACK}`] = true;
return classes;
};

return (
<Switch fallback={<h1 classList={getClasslist()}>{props.children}</h1>}>
<Match when={props.as === 'h2'}>
<h2 classList={getClasslist()}>{props.children}</h2>
</Match>
<Match when={props.as === 'h3'}>
<h3 classList={getClasslist()}>{props.children}</h3>
</Match>
<Match when={props.as === 'h4'}>
<h4 classList={getClasslist()}>{props.children}</h4>
</Match>
<Match when={props.as === 'h5'}>
<h5 classList={getClasslist()}>{props.children}</h5>
</Match>
<Match when={props.as === 'h6'}>
<h6 classList={getClasslist()}>{props.children}</h6>
</Match>
</Switch>
);
};

export default Title;
10 Replies
-=Camille=-
-=Camille=-ā€¢2mo ago
I could also use a Dynamic component like this, but I don't know which is the best solution. šŸ¤”
return (
<Dynamic component={props.as ?? 'h1'} classList={getClasslist()}>
{props.children}
</Dynamic>
);
return (
<Dynamic component={props.as ?? 'h1'} classList={getClasslist()}>
{props.children}
</Dynamic>
);
Jasi šŸ–¤šŸŒˆ
This is the way
-=Camille=-
-=Camille=-ā€¢2mo ago
Is this because it's cleaner and Switch and Dynamic have the same impact? I'm just wondering if the Dynamic component has a bigger impact. The reason is that this Title will be used everywhere in my website, and I would rather not impact the performance too much. šŸ˜“ This title is not really dynamic, I just want to render a different heading level once and then leave it like this šŸ˜„
Alex Lohr
Alex Lohrā€¢2mo ago
Dynamic certainly has the higher impact, but Solid's performance is so good that you'll hardly notice even with 1000 headlines.
-=Camille=-
-=Camille=-ā€¢2mo ago
Okay thanks. I'm just wondering if using a Dynamic component to just return a different header depending on the prop is a bad idea, since I actually don't need dynamism. Should I just use the prop outside a reactive context to improve the performance? I guess I would lose reactivity, but gain performance by removing the reactivity checks. I know I would not notice any significant difference, but I would like my code and websites to have the least performance impact as possible. I'm striving for eco-friendliness. šŸ˜„
bigmistqke šŸŒˆ
bigmistqke šŸŒˆā€¢2mo ago
i think Dynamic is a good idea. having non-reactive props is introducing mental overhead and possible bugs. if ur solo dev it might be fine for now, but then still I wouldn't start introducing non-reactive props, thinking about maintaining the code later down the line. The ecological fingerprint of Dynamic will be very tiny, especially if it doesn't upload often. I would consider it a micro-optimisation.
-=Camille=-
-=Camille=-ā€¢2mo ago
Okay, thanks for the feedback! You are right, I also thought that I would have to put warnings for future devs that component would not react to changes to the as prop. It's probably too much risk for a low impact on the website's performance. Thanks for the help šŸ™‚ šŸ™
bigmistqke šŸŒˆ
bigmistqke šŸŒˆā€¢2mo ago
ur welcome!
duk
dukā€¢2mo ago
You could also use document.createElement(props.as) and return that from your component if you don't need it to be dynamic. In Solid DOM and JSX nodes are interchangable.
-=Camille=-
-=Camille=-ā€¢2mo ago
Thank you for the advice!