Skip to content

Commit

Permalink
Merge branch 'GABRYCA-svelte-5' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
TheDahoom committed Nov 15, 2024
2 parents 7a33407 + ffaa533 commit f4ea247
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 62 deletions.
97 changes: 87 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
> [!CAUTION]
> Svelte 5 support is still pretty much experimental, it requires heavy testing and might not work as expected.
<p align="center">
<img src="https://github.com/TheDahoom/Sveltekit-seo/assets/105564371/338fd0ad-120f-4b4b-ac00-d56e0b765724" alt="sk-seo logo" />
</p>
Expand All @@ -24,21 +27,96 @@ npm i -D sk-seo
```
If you're using @adapter-static, make sure to follow <a href="#prerendering">this</a>
## Usage
import the file
Add the component to your layout file (eg: `+layout.svelte`).
```svelte
<!-- +layout.svelte -->
<script>
import Seo from 'sk-seo';
</script>
<Seo />
```
This component makes use of `$page.data` **stores**. So we should use some load functions.
> [!NOTE]
> These will be automatically picked up by the component and used to fill in the meta tags.
Add a `+layout.js` file alongside your `+layout.svelte` with a load function and return data with the
SEO/Meta that you need:
```js
// +layout.js
export const load = async ({ url }) => {
// OPTIONAL: You can use url.origin to get the base URL,
// or even url.href to get the full URL.
// (For example, to get URLs of images in your /static folder), like this:
// imageURL: `${url.origin}/image.jpg`
return {
title: 'Dahoom - Official',
description: 'The official website of Dahoom',
keywords: 'dahoom, official, website'
// ... and more
}
}
```
Then place this code anywhere in your svelte file

> [!TIP]
> You can override your `+layout.js` meta from any `+page.js`.
Make sure to add a `+page.js` with a load function to all your pages with the SEO/Meta that you need:
```js
// contacts/+page.js
export const load = async ({ url }) => {
// Title, description and keywords set here will replace the title set in +layout.js while visiting /contacts
return {
title: 'Contacts',
description: 'Where to contact Dahoom AlShaya, whether for business needs or general inquiries',
keywords: 'Contact, business, inquiries',
}
}
```

> [!TIP]
> This also works with `+layout.server.js` and `+page.server.js`.
## DEPRECATED USAGE (Not recommended, duplicates meta tags)
Put a `<Seo />` tag in each page you want to have SEO for.
> [!WARNING]
> This's fine as long as you're making a single-page website (such as, just an homepage). But if you're making a
> multi-page website, you should use the previous method!
```svelte
<!-- contacts/+page.svelte -->
<script>
import Seo from 'sk-seo';
</script>
<Seo
title="Contact"
description="Where to contact Dahoom AlShaya, whether for business needs or general inquiries"
keywords="Contact, business, inquiries"
/>
```

> [!CAUTION]
> Using `<Seo />` on each page will duplicate meta tags. This's why we recommend using the first method
> (`load` functions and `<Seo />` only in your `+layout.svelte`).
### Conflicting stores/load return values

You could even combine stores (with any name that you want, just make sure to use the same name that you return from
your `+layout.js/+page.js` load function) and manual input if you really have to:
```svelte
<!-- +layout.svelte -->
<script>
import Seo from 'sk-seo';
import { page } from '$app/stores';
</script>
<Seo
title={$page.data.customTitle ?? ''} <!-- CUSTOM NAME: title now points to $page.data.customTitle store -->
description="Where to contact Dahoom AlShaya, whether for business needs or general inquiries"
keywords="Contact, business, inquiries"
/>
```

## Standard Parameters
| Parameter | Description | Type | Default |
| ------------- | ----------------------- | ------- | ----------------------- |
Expand All @@ -63,6 +141,7 @@ All these choices are optional
| `author`| Represents the author of the page | string | ~ |
| `socials`| An array of social media links for SchemaOrg | Array | ~ |
| `name`| The name to be used for SchemaOrg | string | ~ |
| `type`| The type of the page (website, article, [etc](https://schema.org/docs/full.html)) | string | website |

## How it works
The component uses `<svelte:head>` to place meta tags that are filled with sveltekit $page and inputted variables, this means that a lot of the tags are automatically filled for you for each page in your website. An example of this is `og:url`, which requires the url of the current page:
Expand All @@ -83,9 +162,6 @@ A lot of SEO is repeated boilerplate for twitter, open graph and schemaOrg. This

I initially made this for my personal website and decided to open source it to so that no one has to go through the headache I did to make sure everything is functional.

## Keywords?!
It's optional for anyone who wants to use it. Google doesn't rely on keywords anymore but apparently bing still does put a tiny weight on it. I personally use keywords for my personal website's search function.

## Prerendering
If you are using adapter-static, you need to add your base URL in `svelte.config.js`, otherwise `$page.url` will default to `http://sveltekit-prerender`.
```js
Expand All @@ -104,15 +180,16 @@ If you're behind `Cloudflare` and find yourself with duplicated meta tags, then

`Speed -> Optimization -> Content Optimization -> Auto Minify -> UNCHECK HTML`

## License
[MIT License](https://github.com/TheDahoom/Sveltekit-seo/blob/main/LICENSE)

## Extra
If you have any ideas of fixes/imporvements/features that could be added then please suggest them [here](https://github.com/TheDahoom/Sveltekit-seo/discussions/3)
> [!WARNING]
> Still having duplicated meta? Make sure that you're not using `<Seo />` in each page.
## Credits
- Thanks to [GABRYCA](https://github.com/GABRYCA) for implementing a new cool new approach, svelte 5 and the multitude of additions that came with them!
- Thanks to [ArchangelGCA](https://github.com/ArchangelGCA) for the multiple quality of life imporvements and fixes!
- Thanks to [RodneyLab](https://github.com/rodneylab) for his [blog](https://rodneylab.com/adding-schema-org-markup-to-sveltekit-site/) post which taught me about jsonLd and for suggesting an interesting snippet of code to render jsonLd

## License
[MIT License](https://github.com/TheDahoom/Sveltekit-seo/blob/main/LICENSE)

## As seen on
[Svelte Blog](https://svelte.dev/blog/whats-new-in-svelte-may-2024)
Expand Down
147 changes: 97 additions & 50 deletions SEO.svelte
Original file line number Diff line number Diff line change
@@ -1,83 +1,130 @@
<script>
import { page } from "$app/stores";
export let title = "", description = "", keywords = "", canonical = "", siteName = "", imageURL = "", logo = "",
author = "", name = "";
export let index = true, twitter = true, openGraph = true;
export let schemaOrg = false, schemaType = ['Person', 'Organization'];
export let socials = [], jsonld = {};
/**
* @type {{
* children?: import('svelte').Snippet,
* title?: string,
* description?: string,
* keywords?: string,
* canonical?: string,
* siteName?: string,
* imageURL?: string,
* logo?: string,
* type?: string,
* author?: string,
* name?: string,
* index?: boolean,
* twitter?: boolean,
* openGraph?: boolean,
* schemaOrg?: boolean,
* schemaType?: string[],
* socials?: string[],
* jsonld?: Record<string, any>
* }}
* */
let {
title = $page.data.title ?? "",
description = $page.data.description ?? "",
keywords = $page.data.keywords ?? "",
canonical = $page.data.canonical ?? "",
siteName = $page.data.siteName ?? "",
imageURL = $page.data.imageURL ?? "",
logo = $page.data.logo ?? "",
type = $page.data.type ?? "website",
author = $page.data.author ?? "",
name = $page.data.name ?? "",
index = $page.data.index ?? true,
twitter = $page.data.twitter ?? true,
openGraph = $page.data.openGraph ?? true,
schemaOrg = $page.data.schemaOrg ?? false,
schemaType = $page.data.schemaType ?? ['Person', 'Organization'],
socials = $page.data.socials ?? [],
jsonld = $page.data.jsonld ?? {},
children
} = $props();
let Ld = {
// WORKAROUND TO ENSURE REACTIVITY
let finalTitle = $derived($page.data.title ?? title), finalDescription = $derived($page.data.description ?? description),
finalKeywords = $derived($page.data.keywords ?? keywords), finalCanonical = $derived($page.data.canonical ?? canonical),
finalSiteName = $derived($page.data.siteName ?? siteName), finalImageURL = $derived($page.data.imageURL ?? imageURL),
finalLogo = $derived($page.data.logo ?? logo), finalType = $derived($page.data.type ?? type),
finalAuthor = $derived($page.data.author ?? author), finalName = $derived($page.data.name ?? name);
let finalIndex = $derived($page.data.index ?? index), finalTwitter = $derived($page.data.twitter ?? twitter),
finalOpenGraph = $derived($page.data.openGraph ?? openGraph), finalSchemaOrg = $derived($page.data.schemaOrg ?? schemaOrg);
let finalSchemaType = $derived($page.data.schemaType ?? schemaType), finalSocials = $derived($page.data.socials ?? socials),
finalJsonld = $derived($page.data.jsonld ?? jsonld);
let LdScript = $derived(`<script type="application/ld+json">${JSON.stringify({
"@context": "https://schema.org",
"@type": schemaType.length > 1 ? schemaType : schemaType[0],
"name": name,
"@type": finalSchemaType.length > 1 ? finalSchemaType : finalSchemaType[0],
"name": finalName,
"url": $page.url.origin,
"image": imageURL,
"image": finalImageURL,
"logo": {
"@type": "ImageObject",
"url": logo,
"url": finalLogo,
"width": 48,
"height": 48
},
"sameAs": socials
};
Ld = { ...Ld, ...jsonld };
let LdScript = `<script type="application/ld+json">${JSON.stringify(Ld)}${'<'}/script>`;
"sameAs": finalSocials
, ...finalJsonld}
)}${'<'}/script>`);
</script>
<svelte:head>
{#if title !== ""}
{#if imageURL}
<meta name="robots" content={index ? "index, follow, max-image-preview:large" : "noindex"}>
{#if finalTitle !== ""}
{#if finalImageURL}
<meta name="robots" content={finalIndex ? "index, follow, max-image-preview:large" : "noindex"}>
{:else}
<meta name="robots" content={index ? "index, follow" : "noindex"}>
<meta name="robots" content={finalIndex ? "index, follow" : "noindex"}>
{/if}
<title>{title}</title>
<link rel="canonical" href="{canonical === '' ? $page.url : canonical}">
<title>{finalTitle}</title>
<link rel="canonical" href="{finalCanonical ? finalCanonical : $page.url.href}">
{/if}
{#if description !== ""}
<meta name="description" content="{description}">
{#if finalDescription !== ""}
<meta name="description" content="{finalDescription}">
{/if}
{#if keywords !== ""}
<meta name="keywords" content="{keywords}">
{#if finalKeywords !== ""}
<meta name="keywords" content="{finalKeywords}">
{/if}
{#if author !== ""}
<meta name="author" content="{author}">
{#if finalAuthor !== ""}
<meta name="author" content="{finalAuthor}">
{/if}
{#if openGraph}
{#if siteName !== ""}
<meta property="og:site_name" content="{siteName}">
{#if finalOpenGraph}
{#if finalSiteName !== ""}
<meta property="og:site_name" content="{finalSiteName}">
{/if}
{#if title !== ""}
<meta property="og:url" content="{$page.url}">
<meta property="og:type" content="website">
<meta property="og:title" content="{title}">
{#if finalTitle !== ""}
<meta property="og:url" content="{$page.url.href}">
<meta property="og:type" content="{finalType}">
<meta property="og:title" content="{finalTitle}">
{/if}
{#if description !== ""}
<meta property="og:description" content="{description}">
{#if finalDescription !== ""}
<meta property="og:description" content="{finalDescription}">
{/if}
{#if imageURL !== ""}
<meta property="og:image" content="{imageURL}">
{#if finalImageURL !== ""}
<meta property="og:image" content="{finalImageURL}">
{/if}
{#if logo !== ""}
<meta property="og:logo" content="{logo}">
{#if finalLogo !== ""}
<meta property="og:logo" content="{finalLogo}">
{/if}
{/if}
{#if twitter}
{#if title !== ""}
{#if finalTwitter}
{#if finalTitle !== ""}
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="{$page.url.host}">
<meta property="twitter:url" content="{$page.url}">
<meta name="twitter:title" content="{title}">
<meta property="twitter:domain" content="{$page.url.hostname}">
<meta property="twitter:url" content="{$page.url.href}">
<meta name="twitter:title" content="{finalTitle}">
{/if}
{#if description !== ""}
<meta name="twitter:description" content="{description}">
{#if finalDescription !== ""}
<meta name="twitter:description" content="{finalDescription}">
{/if}
{#if imageURL !== ""}
<meta name="twitter:image" content="{imageURL}">
{#if finalImageURL !== ""}
<meta name="twitter:image" content="{finalImageURL}">
{/if}
{/if}
<slot/>
{#if schemaOrg || socials[0] !== undefined || logo !== "" || name !== ""}
{@render children?.()}
{#if finalSchemaOrg && (finalSocials[0] !== undefined || finalLogo !== "" || finalName !== "")}
{@html LdScript}
{/if}
</svelte:head>
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sk-seo",
"version": "0.4.4",
"version": "0.5.0-next",
"description": "A lightweight, no depenencies, Sveltekit SEO component to save your time",
"main": "SEO.svelte",
"exports": {
Expand All @@ -26,5 +26,8 @@
"url": "git+https://github.com/TheDahoom/Sveltekit-seo.git"
},
"author": "Dahoom AlShaya DahoomA@dahoom.com",
"license": "MIT"
"license": "MIT",
"peerDependencies": {
"svelte": ">=5.0.0"
}
}

0 comments on commit f4ea247

Please sign in to comment.