sveltejs/svelte

`@html` content in `<svelte:head>` is duplicated

winston0410 opened this issue ยท 25 comments

Describe the bug

simple json object of json-ld will be duplicated with injecting into the document with <svelte:head>. The tag will be rendered twice.

I use a function like this:

export const mkSchema = (thing: Schema) => {
	return `<script type="application/ld+json">${JSON.stringify(thing, null, 2)}</script>`;
};

And render like this:

<svelte:head>
    {@html mkSchema(orgSchema)}
</svelte:head>

Reproduction

https://github.com/winston0410/duplicate-head

Logs

No response

System Info

System:
    OS: macOS 11.4
    CPU: (8) arm64 Apple M1
    Memory: 123.59 MB / 8.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 17.8.0 - /opt/homebrew/bin/node
    Yarn: 1.22.17 - /opt/homebrew/bin/yarn
    npm: 8.5.5 - /opt/homebrew/bin/npm
  Browsers:
    Brave Browser: 99.1.36.119
    Safari: 14.1.1
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.34 
    @sveltejs/kit: next => 1.0.0-next.310 
    svelte: ^3.44.0 => 3.47.0

Severity

blocking all usage of SvelteKit

Additional Information

It will greatly affect the SEO of sveltekit as structured data cannot be used

Does it also happen when you put the script tag into <svelte:head> directly without using @html ?

note: to avoid potential security issues/rendering errors, you should escape the content of the script tag ( see eg. https://github.com/sveltejs/kit/blob/01221d30a78483a212ccb2d3e9b2ee0094b916cb/packages/kit/src/utils/escape.js#L44)

@dominikg It will not happen if you do that directly, but then the content of the object cannot be expanded.

A repro for that behavior:

https://github.com/winston0410/duplicate-head/tree/use-head

looks like you already found #1607 and posted there before opening this issue.

This information should have been included in your original report.

Yup I have found that. Should I proceed this issue here, or do it over that one? Because that one is closed right now.

Let's use this issue since the other one is closed. I think that issue was fixed, but some issues remain.

@hbirler you had looked at hydration stuff a decent amount earlier. I'm not sure if you're still interested in these, but this might be an interesting case to take a look at. I confirmed that there's a bug here

@benmccann Thank you so much for your effort in moderating the issue.

@benmccann @dominikg I just added a comment to issue #6463 that seems to relate to this as well.

A workaround for now is to add the json-ld in the body since it's doesn't make a difference for search engine bots.

@abdo643-HULK that works if you are working with json-ld but there are any other things you may want to render in the head with @html. In my case, I'm trying to inject server side css into the head with the <style> tag.

@abdo643-HULK that works if you are working with json-ld but there are any other things you may want to render in the head with @html. In my case, I'm trying to inject server side css into the head with the <style> tag.

Yes that's why I said json-ld :). But you can also inject a style tag into the body and it's still global and valid html. If it's a link tag than you are correct and it isn't valid.
Edit: I don't think it's ideal, but a workaround for now

@abdo643-HULK that works if you are working with json-ld but there are any other things you may want to render in the head with @html. In my case, I'm trying to inject server side css into the head with the <style> tag.

Yes that's why I said json-ld :). But you can also inject a style tag into the body and it's still global and valid html. If it's a link tag than you are correct and it isn't valid.

Edit: I don't think it's ideal, but a workaround for now

Sveltekit doesn't offer an out-of-the-box solution for adding custom tags to the body... but they do give you their svelte:head component which makes head injection straightforward.

I suppose I could make a custom body component and pull all of my custom html out of the base html file and put it in that component, including the style injection. ๐Ÿคทโ€โ™‚๏ธ

@abdo643-HULK that works if you are working with json-ld but there are any other things you may want to render in the head with @html. In my case, I'm trying to inject server side css into the head with the <style> tag.

Yes that's why I said json-ld :). But you can also inject a style tag into the body and it's still global and valid html. If it's a link tag than you are correct and it isn't valid.
Edit: I don't think it's ideal, but a workaround for now

Sveltekit doesn't offer an out-of-the-box solution for adding custom tags to the body... but they do give you their svelte:head component which makes head injection straightforward.

I suppose I could make a custom body component and pull all of my custom html out of the base html file and put it in that component, including the style injection. ๐Ÿคทโ€โ™‚๏ธ

I think you are thinking a little bit complicated if you fetch the resource for example in your layout and just do:

<script>
    export let style;
</script>

{@html style}
<slot/>

This should inject the style tag into your body

I can confirm that the tricks mentioned by @abdo643-HULK works! Just tried that and Google seems to be happy with it

There don't seem to be any duplicates here: https://svelte.dev/repl/ffd783c9b8e54d97b6b7cac6eadace42?version=3.48.0

The duplication happens when you use SSR.

In a component i define some content in svelte.head. If i use this component multiple times on a page. It will add the content multiple times

The duplication happens when you use SSR.

Very critical issue for me. For now, I'm forced to use this dirty temporary workaround to remove duplicates.

hooks/index.js:

export async function handle({event, resolve}) {
	let response = await resolve(event, {
		transformPage: ({ html }) => {
			return html
				.replace(/<link\s+rel="canonical"[^>]*>/, '')
				.replace(/<meta\s+name="description"[^>]*>/, '')
				.replace(/<meta\s+name="keywords"[^>]*>/, '')
				.replace(/<meta\s+property="og:url"[^>]*>/, '')
				.replace(/<meta\s+property="og:title"[^>]*>/, '')
				.replace(/<meta\s+property="og:image"[^>]*>/, '')
				.replace(/<meta\s+property="og:description"[^>]*>/, '')
		}
	})

	return response
}

Any update when this is going to be fixed?
I have a project that depends on it, the workaround mensioned here doesn't work in my use case.

Also depending on this fix.

Fixed in #7745

This should be fixed now in 3.51.0.

@Conduitry using 3.52, the problem persists with latest kit

@thousandsofraccoons thanks for pointing that out.
Could you care to help create a new issue with a repro? Comments on a closed issue will be easily missed out.

@tanhauhau, related issue - #7879