Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<svelte:head> that runs likes <script module> by only running once per component. #15219

Open
HighFunctioningSociopathSH opened this issue Feb 5, 2025 · 3 comments

Comments

@HighFunctioningSociopathSH
Copy link

HighFunctioningSociopathSH commented Feb 5, 2025

Describe the problem

I have a component that is trying to generate some styles through some JS logic and then wants to populate the head element with those styles. The issue is that I want to only populate the head with these styles if the component is actually rendered so the head element doesn't get filled with useless styles. So I thought with my self that I should do what I've been doing up to now for other similar cases. Generate the styles and then put them inside of a <svelte:head> element using {at html} but this time inside the component instead of the +page. But I found out that the <svelte:head> element runs for every instance of the component which meant that If 5 instances of the component where rendered through out the code base, then 5 similar style tags would be in the head element which is not good.

Test.svelte

<script lang="ts">
</script>

<svelte:head>
  <style>
    button {
      background: red;
    }
  </style>
</svelte:head>

+page.svelte

<script lang="ts">
  import Test from "$components/Test/Test.svelte";
</script>

<Test></Test>
<Test></Test>
<Test></Test>
<Test></Test>
<Test></Test>

Image

Describe the proposed solution

Trying to workaround this issue by adding the style tag manually to the document.head doesn't work as well as <svelte:head> because the source html returned from SvelteKit doesn't have my custom style tag in its head element, which can result in a flash of unstyled content.
So I was wondering if its possible to have a <svelte:head> element that runs only once for each component as modules like <script module> tag do.

I will also appreciate any suggestions for a workaround for this issue.

Importance

would make my life easier

@7nik
Copy link
Contributor

7nik commented Feb 5, 2025

What about this

<script module>
	let currentKey = $state(null);
</script>
<script>
	import { onDestroy } from "svelte";
	const localKey = Symbol();
	$effect(() => {		
		currentKey ??= localKey;
	});
	onDestroy(() => {
		if (currentKey === localKey) currentKey = null;  
	});
</script>

<svelte:head>
	{#if currentKey === localKey}
	  <style> h1 { color: green;  } </style>
	{/if}
</svelte:head>

REPL

Edit: You may want to do

        currentKey ??= localKey;
	$effect(() => {		
		currentKey ??= localKey;
	});

to make it SSR-friendly.

@HighFunctioningSociopathSH
Copy link
Author

@7nik wow that looks like a great workaround. Thank you, I'll try it

@HighFunctioningSociopathSH
Copy link
Author

Thanks to you I managed to fix my issue But i still wonder whether we can have something like <svelte:head module> that can do all of this like <script module> does. It would be a cool addition in my opinion. Specially in cases like mine where you would want to create a module from this procedure to avoid having to repeat it for every single component that uses some sort of css in js, which can lead to a bit more coding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants