I am new to SvelteKit and I am not familiar with SSR. I have recently learned that window
is only defined after the client-side has been loaded, which can be determined with the onMount
hook.
But here's something that I am not clear on. Let's say I have the following component:
<script lang="ts">
const getRem = (rem: number) => rem *
parseFloat(getComputedStyle(document.documentElement).fontSize)
const sizeOfSomeOtherElement = getRem(4) // returns the px value of 4rem
const sizeOfThisElement = sizeOfSomeOtherElement * 0.5
</script>
<div style="width: {sizeOfThisElement}rem"></div>
Now I understand in this particular example I can just use CSS calc, but let's pretend that I have to use a JavaScript value for the sake of argument. Here, I want to dynamically get the value of a rem
as it could be different based on the user.
This will throw an error telling me that getComputedStyle
is not defined, as window is not defined. Now, I could delay the rendering of the element and wait until the component is mounted once like so:
<script lang="ts">
let isLoaded = $state(false)
onMount(() => { isLoaded = true })
/* ... other conditionals ... */
</script>
{#if isLoaded)
<div style="width: {sizeOfThisElement}rem"></div>
{/if}
But this delays the rendering of the component by a slight, but noticeable amount. It doesn't seem to be the right solution to me, as I don't need the component to be mounted, I just need the variable window
to be available, which I would think comes before mounting.
What's the best way to tackle this? Am I misunderstanding anything?
There will always be a delay between the server-side provided HTML being rendered and the component script being executed and updating it.
I tried other methods, that are focused on a single element (like bind:this
and use:action
), but they have the same issue.
You would essentially have to run code synchronously while the element is being rendered, to prevent this entirely. It is technically possible to add a plain <script>
by wrapping it in an element but then you are limited to native JS and also cannot reference anything in the component script.
E.g.
<div>
DIV to adjust
<script>
const getRem = rem => rem *
parseFloat(getComputedStyle(document.documentElement).fontSize);
const target = document.currentScript.parentElement;
const sizeOfSomeOtherElement = getRem(4);
target.style.width = (sizeOfSomeOtherElement * 0.5) + 'rem';
</script>
</div>
Approaches like this are sometimes used for dealing with theme switches that are stored in localStorage
to prevent flashes.