Amir Pournasserian
Amir Pournasserian

Reputation: 1674

How can I get current component tag name in Svelte?

I am developing a custom UI component library using Svelte and each component has at least a CSS class which is the same name the component tag name. Also, I have a base component which all other components are using that. Here is the simplified codes:

Base.svelte

<script>
    export let componentName
    export let tag = 'div'
</script>
<svelte:element this={tag} class={componentName}>
    <slot />
</svelte:element>  

Foo.svelte

<script>
    import Base from './Base.svelte'
</script>
<Base tag="b" componentName="Foo">
    <slot />
</Base>

Bar.svelte

<script>
    import Base from './Base.svelte'
</script>
<Base tag="h3" componentName="Bar">
    <slot />
</Base>

App.svelte

<script>
    import Foo from './Foo.svelte'
    import Bar from './Bar.svelte'
</script>

<Foo>
    <Bar>This is Bar 1</Bar>
    <Bar>This is Bar 2</Bar>
</Foo>
<Foo>
    Foo
</Foo>

In above code, I have to set the componentName prop which actually should be a constant. Also, I should always update that in case I change the component's file name.

Is there anyway to get the current component tag name and set it in Base component?

Or, a function that returns the name similar to what we have for the component:

import { current_component } from 'svelte/internal';

Upvotes: 1

Views: 887

Answers (1)

brunnerh
brunnerh

Reputation: 184526

You cannot get the tag name; it is not static and components can be rendered without using a tag at all via <svelte:component> or by just using the constructor in code. There are ways to get the file name, which then could be transformed.

I would approach this via pre-processing. Svelte already provides a way to do this via its config, e.g.

// svelte.config.js
import { basename } from 'path';

/** @type {import('svelte/types/compiler/preprocess').PreprocessorGroup} */
const addComponentName = {
    script: ({ content, filename }) => {
        const componentName = basename(filename, '.svelte');
        return {
            code: `const __componentName = ${JSON.stringify(componentName)};\n`
                + content,
        };
    }
}

export default { preprocess: [addComponentName] };

This adds code that sets a constant __componentName to the file name, which thus can be used in the rest of the code or the markup. You can use whatever name works for you; the underscores are there to prevent potential name collisions with the existing code.

(If you are using TypeScript you have to declare a global for this to prevent errors, as the declaration will be missing from the source files.)

Upvotes: 2

Related Questions