The most important aspect of SEO is to create high-quality content that is widely linked to from around the web. However, there are a few technical considerations for building sites that rank well.
Out of the box
SSR
While search engines have got better in recent years at indexing content that was rendered with client-side JavaScript, server-side rendered content is indexed more frequently and reliably. SvelteKit employs SSR by default, and while you can disable it in handle, you should leave it on unless you have a good reason not to.
SvelteKit’s rendering is highly configurable and you can implement dynamic rendering if necessary. It’s not generally recommended, since SSR has other benefits beyond SEO.
Performance
Signals such as Core Web Vitals impact search engine ranking. Because Svelte and SvelteKit introduce minimal overhead, they make it easier to build high performance sites. You can test your site’s performance using Google’s PageSpeed Insights or Lighthouse. With just a few key actions like using SvelteKit’s default hybrid rendering mode and optimizing your images, you can greatly improve your site’s speed. Read the performance page for more details.
Normalized URLs
SvelteKit redirects pathnames with trailing slashes to ones without (or vice versa depending on your configuration), as duplicate URLs are bad for SEO.
Manual setup
<title> and <meta>
Every page should have well-written and unique <title> and <meta name="description"> elements inside a <svelte:head>. Guidance on how to write descriptive titles and descriptions, along with other suggestions on making content understandable by search engines, can be found on Google’s Lighthouse SEO audits documentation.
A common pattern is to return SEO-related data from page load functions, then use it (as page.data) in a <svelte:head> in your root layout.
Sitemaps
Sitemaps help search engines prioritize pages within your site, particularly when you have a large amount of content. You can create a sitemap dynamically using an endpoint:
An unfortunate reality of modern web development is that it is sometimes necessary to create an Accelerated Mobile Pages (AMP) version of your site. In SvelteKit this can be done by setting the inlineStyleThreshold option...
:{ // since <link rel="stylesheet"> isn't // allowed, inline all stylesinlineStyleThreshold(property)KitConfig.inlineStyleThreshold?:number|undefined
Inline CSS inside a <style> block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file in UTF-16 code units, as specified by the String.length property, to be inlined. All CSS files needed for the page that are smaller than this value are merged and inlined in a <style> block.
[!NOTE] This results in fewer initial requests and can improve your First Contentful Paint score. However, it generates larger HTML output and reduces the effectiveness of browser caches. Use it advisedly.
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
input
the html chunk and the info if this is the last chunk
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
input
the html chunk and the info if this is the last chunk
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
input
the html chunk and the info if this is the last chunk
Replaces text in a string, using a regular expression or search string.
searchValue
A string or regular expression to search for.
replaceValue
A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
('⚡','amp')// dropcss can't handle this character.replace(method)String.replace(searchValue:{[Symbol.replace](string:string,replacer:(substring:string,...args:any[])=>string):string;},replacer:(substring:string,...args:any[])=>string):string(+3overloads)
Replaces text in a string, using an object that supports replacement within a string.
searchValue
A object can search for and replace matches within a string.
Replaces text in a string, using a regular expression or search string.
searchValue
A string or regular expression to search for.
replaceValue
A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
The handle hook runs every time the SvelteKit server receives a request and
determines the response.
It receives an event object representing the request and a function called resolve, which renders the route and generates a Response.
This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).
Applies custom transforms to HTML. If done is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element's opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head% or layout/page components.
input
the html chunk and the info if this is the last chunk
Replaces text in a string, using a regular expression or search string.
searchValue
A string or regular expression to search for.
replaceValue
A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
('⚡','amp')// dropcss can't handle this character.replace(method)String.replace(searchValue:{[Symbol.replace](string:string,replacer:(substring:string,...args:any[])=>string):string;},replacer:(substring:string,...args:any[])=>string):string(+3overloads)
Replaces text in a string, using an object that supports replacement within a string.
searchValue
A object can search for and replace matches within a string.
Replaces text in a string, using a regular expression or search string.
searchValue
A string or regular expression to search for.
replaceValue
A string containing the text to replace. When the {@linkcode searchValue} is a RegExp, all matches are replaced if the g flag is set (or only those matches at the beginning, if the y flag is also present). Otherwise, only the first match of {@linkcode searchValue} is replaced.
It’s a good idea to use the handle hook to validate the transformed HTML using amphtml-validator, but only if you’re prerendering pages since it’s very slow.