I'm creating a Heading
component in svelte
as a part of learning the basics of this framework. The component behavior is pretty straight-forward.
The component will have a prop named level
, which will render the appropriate <h>
tag accordingly.
For eg.
<Heading level={3}> would render <h3>content</h3>
<Heading level={1}> would render <h1>content</h1>
I can achieve this currently with,
<script>
export let level = 3;
</script>
{#if level === 1}
<h1>
<slot></slot>
</h1>
{:else if level === 2}
<h2>
<slot></slot>
</h2>
{:else if level === 3}
<h3>
<slot></slot>
</h3>
{:else if level === 4}
<h4>
<slot></slot>
</h4>
{:else if level === 5}
<h5>
<slot></slot>
</h5>
{/if}
But this kind of feels like a very naive approach.
Is there any better way to achieve this behaviour in svelte ?
I know your question is geared toward feeding a numerical prop to the component to set the heading level, but since you ended your OP with this question:
But this kind of feels like a very naive approach. Is there any better way to achieve this behaviour in svelte ?
...and to benefit future readers, here's a more robust solution to the core problem of wanting a component for generating heading tags.
You could use the Context API to generate heading tags that are completely context-aware and fully automate this, if you don't mind using a wrapper component for each section of content on the page. You would need:
Here's an example:
Section.svelte
<script>
import { setContext, getContext } from 'svelte'
let level
// if we find a context has already been set in this component tree,
// it came from a parent/ancestor instance of Section.svelte
if (getContext('headingLevel')) {
// Increment the context because this is the next nesting level
level = getContext('headingLevel') + 1
setContext('headingLevel', level)
} else {
// otherwise this instance is the first of its kind in the hierarchy
level = 2
setContext('headingLevel', level)
}
</script>
<section>
<slot />
</section>
HeadingTag.svelte
<script>
import { getContext } from 'svelte'
// prop to insert your desired contents into the heading tag
export let message
// get the context, but make sure we can't go higher than <h6>
let level = Math.min(getContext('headingLevel'), 6)
const render = () => `
<h${level}>
${message}
</h${level}>
`
</script>
{@html render()}
Then, in your other components or pages, just use it like so
MyPage.svelte
<Section>
<HeadingTag message={"hello"} />
<!-- renders <h2>hello</h2> -->
<Section>
<HeadingTag message={"hello"} />
<!-- renders <h3>hello</h3> -->
<Section>
<HeadingTag message={"hello"} />
<!-- renders <h4>hello</h4> -->
</Section>
</Section>
</Section>
<Section>
<HeadingTag message={"hello"} />
<!-- renders <h2>hello</h2> -->
</Section>
This will also work seamlessly with however your components are nested.
Note that my example has it set it up to start at <h2>
with the assumption that every page only has a single <h1>
within its <main>
, and that doesn't require this kind of automation. But you can adapt it to your use case as needed, e.g. if you want it to start off with an <h1>
at the top-level...
Section.svelte
<script>
import { setContext, getContext } from 'svelte'
let level
if (getContext('headingLevel')) {
level = getContext('headingLevel') + 1
setContext('headingLevel', level)
} else {
// this and the HTML below are the only things that changed
level = 1
setContext('headingLevel', level)
}
</script>
{#if level === 1}
<main>
<slot />
</main>
{:else}
<section>
<slot />
<section>
{/if}
Credit where it's due and for further reading for those interested: I adopted this solution in Svelte from a React-based example in this article by Heydon Pickering:
https://medium.com/@Heydon/managing-heading-levels-in-design-systems-18be9a746fa3
Try this:
<script>
export let level = 3;
let displayText = "<h" + level + ">" +
"Sample header text" +
"</h" + level + ">";
</script>
<main>
<div>
{@html displayText}
</div>
</main>
You can build your html out as a string by concatenating the value of level into the tag, then display it using the "@html" variable annotation, which interprets your string as html, rather than plain text.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With