{"id":28273,"date":"2021-07-27T18:08:04","date_gmt":"2021-07-27T18:08:04","guid":{"rendered":"http:\/\/Setup-a-Developer-Blog-with-Social-Images-using-Sveltekit"},"modified":"2021-07-27T18:08:04","modified_gmt":"2021-07-27T18:08:04","slug":"setup-a-developer-blog-with-social-images-using-sveltekit","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/","title":{"rendered":"Setup a Developer Blog with Social Images using SvelteKit"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>The end goal for this article is to implement a blog site that includes the use of open graph images for social sharing. It will consist of a:<\/p>\n<ul>\n<li>A Home page with a list of some articles.<\/li>\n<li>An article page to allow the user to read the articles.<\/li>\n<\/ul>\n<p>Each of these pages will have its own social image. To accomplish this, you\u2019ll set the corresponding meta tag into the <code>&lt;head&gt;<\/code> of the current page using svelte features.<\/p>\n<p>You can check out <a href=\"https:\/\/github.com\/matiasfha\/sveltekit-blog\">this repository<\/a> that will get you to the end result of this article, clone it, run it and play around to grasp the way of work with SvelteKit, or you can just play around with this codesandbox.<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/focused-bartik-jpddg?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=%2Fsrc%2Froutes%2Findex.svelte&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"SvelteKit Blog with Social Images\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n  <div class=\"wp-block-cloudinary-markdown \"><h1>\u00bfWhat is Sveltekit?<\/h1>\n<p>SvelteKit is Svelte\u2019s take on the web application framework. This is the new (still in beta) official way to develop applications with Svelte. It is packed with features like routing, layouts, stage management, API routes, SSG, and SSR. If you come from React or Vue world, SvelteKit is the counterpart of Nextjs or Nuxt.<\/p>\n<p>SvelteKit helps you to make static sites, server-rendered sites, and even hybrid static\/server-rendered apps. It delivers an outstanding developer experience and fast user experience like the Svelte philosophy describes.<\/p>\n<p>In general words, SvelteKit is a tool to take your Svelte code and transform it into a node app or static files. You can consider Svelte as the underlying language and SvelteKit the set that brings the server-side and some options about how an application should be architected.<\/p>\n<h2>What are Social Images<\/h2>\n<p>One big part of content creation is to share your content on social media, but just tweeting about your newest post is not enough to stand out in a never-ending stream of content.<\/p>\n<p>One way to stand out on all of that noise is by sharing images or \u201c<a href=\"https:\/\/ogp.me\">Open Graph Images<\/a>\u201d for your content.<\/p>\n<p>These images are used by different social network applications to create a \u201ccontent card\u201d and show some kind of preview of the link you\u2019re sharing.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.craft.do\/user\/full\/0a4fa0f6-6e08-58de-6dbc-6c56902ee35d\/doc\/0975E901-EF6C-4C06-87ED-0742402C1FF2\/3D783C62-3CCA-44DE-8C63-6992A8AED80C_2\/Screen%20Shot%202021-07-05%20at%2023.30.39.png\" alt=\"Screen Shot 2021-07-05 at 23.30.39.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1082\" height=\"526\"\/><\/p>\n<p>You can manually create these images for each post you publish in your post, but it is a significant burden that can quickly slow down your publishing workflow. But, we are developers, and we love to craft solutions based on automation.<\/p>\n<p>By the end of this article, you\u2019ll be able to generate social media images by using Cloudinary transformation API as part of your SvelteKit site. You\u2019ll also learn how to get up to speed with SvelteKit by creating your first blog.<\/p>\n<h2><strong>Develop your blog<\/strong><\/h2>\n<p>This section will drive you through the steps required to create the blog described at the top of the article. This particular implementation uses:<\/p>\n<ul>\n<li>Prismic as headless CMS to write and deliver the articles<\/li>\n<li>tailwindcss for styling<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/res.craft.do\/user\/full\/0a4fa0f6-6e08-58de-6dbc-6c56902ee35d\/doc\/0975E901-EF6C-4C06-87ED-0742402C1FF2\/6EC54153-AB56-46CF-BC5B-93935F23A442_2\/Screen%20Shot%202021-07-05%20at%2023.36.27.png\" alt=\"Screen Shot 2021-07-05 at 23.36.27.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"3352\" height=\"1934\"\/><\/p>\n<p>To start, we just need to run the SvelteKit scaffold script.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">init<\/span> <span class=\"hljs-selector-tag\">svelte<\/span><span class=\"hljs-keyword\">@next<\/span> my-blog\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This will show you a message saying that this is beta software and also a prompt asking what template you want to use. Use the arrow keys to choose <strong>SvelteKit<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/matiasfha\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1626816718\/SvelteKit Prompt.png\" alt=\"SvelteKit Prompt\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1516\" height=\"552\"\/><\/p>\n<p>Then you\u2019ll be asked if you want to use Typescript. For this demo, you can go with plain old javascript. Choose No.\nFinally, select yes to the next questions: Add ESLint and  Add Prettier.<\/p>\n<p>Then to run the base skeleton just go into the new folder, install dependencies and run the  app<\/p>\n<pre class=\"js-syntax-highlighted\"><code>cd my-blog\nnpm install\nnpm run dev\n<\/code><\/pre>\n<p>Now go ahead and visit <code>http:\/\/localhost:3000<\/code> in your browser, and you\u2019ll see the base SvelteKit project running.<\/p>\n<p>Now let\u2019s set up tailwindcss to implement the base layout. For this purpose, you can use <a href=\"https:\/\/github.com\/svelte-add\/tailwindcss\"><code>svelte-add<\/code><\/a>   so just run in console<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npx<\/span> <span class=\"hljs-selector-tag\">svelte-add<\/span><span class=\"hljs-keyword\">@latest<\/span> tailwindcss\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>With that, you are ready to go.<\/p>\n<p>After the scaffold process a few files were created. You will get rid of some of this but let\u2019s check them out first.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/matiasfha\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1626949617\/Screen_Shot_2021-07-22_at_06.26.23_kxaxle.png\" alt=\"SvelteKit File Structure\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"974\" height=\"1112\"\/><\/p>\n<p>From the files there you\u2019ll focus will be under the <code>lib<\/code> and <code>routes<\/code> folder.\nIn SvelteKit every page is an Svelte component, to create a page, just add it to under <code>src\/routes<\/code>. In this case the scaffold created two pages for you. <code>about.svelte<\/code> and <code>index.svelte<\/code>.<\/p>\n<blockquote>\n<p>You can get rid of the <code>about.svelte<\/code> file since you\u2019ll not use it in this project.<\/p>\n<\/blockquote>\n<p>There is also another important file: <code>__layout.svelte<\/code>. This file enables you to create the base layout structure that will be shared across all your pages.<\/p>\n<p>Finally, there is another default folder: <code>src\/lib<\/code> this folder is where you\u2019ll store or save the components or utilities you need in the application. SvelteKit makes the content of this <a href=\"https:\/\/kit.svelte.dev\/docs#modules-$lib\">folder accessible with an alias: <code>$lib<\/code><\/a> so if you need to import something from this folder you just use <code>import Component from '$lib\/Componente.svelte<\/code> since is an absolute path.\nBy default there are some files under this folder like an Svelte component: <code>Counter.svelte<\/code> and an empty javascript file <code>form.js<\/code>.<\/p>\n<blockquote>\n<p>Is important to recall that Svelte manage the components in a single file that have 3 different sections. The HTML where you structure your component, the script tag to define the javascript logic and the style tag to define the scoped css. Check more information about <a href=\"https:\/\/svelte.dev\/docs#Component_format\">Svelte components  here<\/a>.<\/p>\n<\/blockquote>\n<p>Your focus will be in the file under the <code>src<\/code> folder, there you\u2019ll find a few other files:<\/p>\n<ul>\n<li>\n<code>app.html<\/code>: The base HTML structure were the application will be mounted.<\/li>\n<li>\n<code>app.postcss<\/code>: The main postcss configuration. Here you can see tailwind configuration. You can edit this file if you need some extra utilities for your css.<\/li>\n<li>hooks.js: This is an optional file to define some functions that will run in the server. You can read more about the hooks <a href=\"https:\/\/kit.svelte.dev\/docs#hooks\">in the official documentation<\/a>. You\u2019ll not be working with hooks in this particular project.<\/li>\n<\/ul>\n<p>To start, let\u2019s get rid of some of this default files:<\/p>\n<ul>\n<li>Delete the content of the <code>src\/lib<\/code> folder<\/li>\n<li>Delete the file <code>about.svelte<\/code>\n<\/li>\n<li>Delete the content of <code>__layout.svelte<\/code>  and the content of <code>index.svelte<\/code>.<\/li>\n<\/ul>\n<p>Now, let\u2019s start by defining the layout of the blog and think about the components,<\/p>\n<p>In the layout file you will use the <code>&lt;slot \/&gt;<\/code> component to define where the content of the pages will be rendered.<\/p>\n<blockquote>\n<p>If you come from React world, the <code>&lt;slot \/&gt;<\/code> component is very similar to the <code>children<\/code> prop. Read more about <code>&lt;slot \/&gt;<\/code> <a href=\"https:\/\/svelte.dev\/docs#slot\">in the official docs<\/a>.<\/p>\n<\/blockquote>\n<p>Add the content of <a href=\"https:\/\/gist.github.com\/matiasfha\/a52a4b9ccb650ed7eb8f046c118e9618\">this gist<\/a> to the layout file. That code defines the structure of the page including a header with a navigation section, a footer, and the main container.<\/p>\n<p>Now you can add the first page under <code>src\/routes\/index.svelte<\/code> Check the source code of <a href=\"https:\/\/gist.github.com\/matiasfha\/571a6019207978cec844c41ac5e51562\">this example implementation directly here<\/a>.<\/p>\n<p>The site will list the articles retrieved from <a href=\"https:\/\/prismic.io\">Prismic<\/a> on this home page. Here is where you\u2019ll use the new features of SvelteKit.<\/p>\n<blockquote>\n<p><a href=\"https:\/\/prismic.io\">Prismic<\/a> is a headless CMS that easily integrates with your choose technology to manage and deliver the content. In this case, Prismic is used in a very contrived way to store and provide the website\u2019s articles.<\/p>\n<\/blockquote>\n<p>To start, SvelteKit has the concept of <strong>endpoints<\/strong> These are \u201cspecial\u201d modules that live under <code>src\/route<\/code> are unique because instead of exporting a Svelte component, these routes export a function that corresponds with the HTTP methods.<\/p>\n<p>In this example, the home page will render a list of articles. The page will hit an internal endpoint under \/<code>API\/blog\/articles<\/code> to get those. This endpoint will return a JSON structure with the data from Prismic.<\/p>\n<p>These are just standard javascript functions, as you can see in this example.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ \/api\/blog\/articles.json<\/span>\n<span class=\"hljs-keyword\">import<\/span> Client, { Predicates } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/PrismicClient'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">get<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> Client.query(Predicates.at(<span class=\"hljs-string\">'document.type'<\/span>, <span class=\"hljs-string\">'article'<\/span>));\n\t<span class=\"hljs-keyword\">const<\/span> result = response.results.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">r<\/span>) =&gt;<\/span> {\n\t\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t\t...r.data,\n\t\t\t<span class=\"hljs-attr\">id<\/span>: r.id,\n\t\t\t<span class=\"hljs-attr\">href<\/span>: r.href,\n\t\t\t<span class=\"hljs-attr\">uid<\/span>: r.uid,\n\t\t\t<span class=\"hljs-attr\">featured<\/span>: r.data.featured\n\t\t};\n\t});\n\t<span class=\"hljs-keyword\">const<\/span> { featured, articles } = result?.reduce(\n\t\t<span class=\"hljs-function\">(<span class=\"hljs-params\">acc, current<\/span>) =&gt;<\/span> {\n\t\t\t<span class=\"hljs-keyword\">if<\/span> (current.featured) {\n\t\t\t\tacc.featured = current;\n\t\t\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t\t\tacc.articles.push(current);\n\t\t\t}\n\t\t\t<span class=\"hljs-keyword\">return<\/span> acc;\n\t\t},\n\t\t{ <span class=\"hljs-attr\">featured<\/span>: <span class=\"hljs-literal\">null<\/span>, <span class=\"hljs-attr\">articles<\/span>: &#91;] }\n\t);\n\n\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t<span class=\"hljs-attr\">body<\/span>: {\n\t\t\tfeatured,\n\t\t\tarticles\n\t\t}\n\t};\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This exported function is mapped to the get method. It uses the <code>PrismicClient<\/code> methods that you <a href=\"https:\/\/github.com\/matiasfha\/sveltekit-blog\/blob\/main\/src\/lib\/PrismicClient.ts\">can see here<\/a> and just fetch the data, parse it and then return the new object in the body of the request.<\/p>\n<p>Now, how the home page will get this data?. A page, that is, in fact, a Svelte component, can define an export a  function called <code>load<\/code> that will run before the component is created. The trick here is that this function runs server-side and also client-side, allowing you to retrieve data on build time if you want to get an SSG page.<\/p>\n<p>You can just add a new script tag to the home page with the load function to accomplish this feat. Why a new script tag? Since this function runs before the component is rendered, it lives in a different context <code>&lt;script context=&quot;module&quot;&gt;<\/code>.<\/p>\n<p>So, open your <code>src\/routes\/index.svelte<\/code> component and add the load function that perform the fetch request to <code>\/api\/blog\/articles<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">context<\/span>=<span class=\"hljs-string\">\"module\"<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">load<\/span>(<span class=\"hljs-params\">{ fetch }<\/span>) <\/span>{\n\t\t<span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-string\">`\/api\/blog\/articles.json`<\/span>;\n\t\t<span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(url);\n\t\t<span class=\"hljs-keyword\">const<\/span> { articles, featured } = <span class=\"hljs-keyword\">await<\/span> res.json();\n\n\t\t<span class=\"hljs-keyword\">if<\/span> (res.ok) {\n\t\t\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t\t\t<span class=\"hljs-attr\">status<\/span>: res.status,\n\t\t\t\t<span class=\"hljs-attr\">props<\/span>: {\n\t\t\t\t\tarticles,\n\t\t\t\t\tfeatured\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t\t<span class=\"hljs-attr\">status<\/span>: res.status,\n\t\t\t<span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">`Could not load <span class=\"hljs-subst\">${url}<\/span>, status: <span class=\"hljs-subst\">${res.status}<\/span>`<\/span>)\n\t\t};\n\t}\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>As you can see, retrieve data for your page is really straightforward. Now that the <code>props<\/code> object is returned, it will be available for your component, which means that in the Svelte component under this page, you can access  <code>articles<\/code> and <code>featured<\/code> objects.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">import<\/span> PrismicDOM <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'prismic-dom'<\/span>;\n\t<span class=\"hljs-keyword\">import<\/span> ArticleCard <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/components\/ArticleCard.svelte'<\/span>;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> articles: Article&#91;];\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> featured: Article;\n\t<span class=\"hljs-keyword\">const<\/span> list = &#91;...articles].splice(<span class=\"hljs-number\">1<\/span>, articles.length);\n\t<span class=\"hljs-keyword\">const<\/span> listHeader = articles&#91;<span class=\"hljs-number\">0<\/span>];\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Since the data comes from Prismic, you need a helper to parse and render the content of the rich text field. This page also uses a component to render a card. The component can be found in <a href=\"https:\/\/gist.github.com\/matiasfha\/889fef7c44de188b9f17db7a9a59a415\"><code>src\/lib\/components\/ArticleCard.svelte<\/code><\/a><\/p>\n<p>Let\u2019s check how to render the article.<\/p>\n<p>To render an article, the site uses another SvelteKit feature called <strong>dynamic parameters<\/strong>. This allows you to create routes that accept a parameter like the <code>slug<\/code> or <code>uid<\/code> of the article.<\/p>\n<p>The <strong>dynamic parameter<\/strong> let you load a particular element based on some identifier. To do this, let\u2019s create the dynamic page under <code>src\/route\/blog\/[uid].svelte<\/code><\/p>\n<p>This page will get the <code>uid<\/code> from the URL and pass it down to the API to retrieve the corresponding data. This is done again in the <code>load<\/code> function of the page.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">context<\/span>=<span class=\"hljs-string\">\"module\"<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">load<\/span>(<span class=\"hljs-params\">{ fetch, page }<\/span>) <\/span>{\n\t\t<span class=\"hljs-keyword\">const<\/span> { uid } = page.params;\n\t\t<span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-string\">`\/api\/blog\/<span class=\"hljs-subst\">${uid}<\/span>.json`<\/span>;\n\t\t<span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(url);\n\t\t<span class=\"hljs-keyword\">const<\/span> article = <span class=\"hljs-keyword\">await<\/span> res.json();\n\t\t<span class=\"hljs-keyword\">if<\/span> (res.ok) {\n\t\t\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t\t\t<span class=\"hljs-attr\">status<\/span>: res.status,\n\t\t\t\t<span class=\"hljs-attr\">props<\/span>: {\n\t\t\t\t\tarticle\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t\t<span class=\"hljs-attr\">status<\/span>: res.status,\n\t\t\t<span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">`Could not load <span class=\"hljs-subst\">${url}<\/span>, status: <span class=\"hljs-subst\">${res.status}<\/span>`<\/span>)\n\t\t};\n\t}\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This load function will get the <code>uid<\/code> and use it to run the endpoint get function defined in <code>API\/blog\/[uid].json.ts<\/code> that perform the query to Prismic based on the <code>uid<\/code> parameter.<\/p>\n<p>Then, this page will have access to the corresponding article instance to render it as is shown here.<\/p>\n<p>Here is where the social images come into play.<\/p>\n<h1>Setup Social Images<\/h1>\n<p>Some of the most shared content of a site or blog is the articles, so each piece should have its own social images (and SEO data) based on the content that is actually present. This can be easily accomplished by a neat Svelte feature: the <code>&lt;svelte:head&gt;<\/code> component.<\/p>\n<p>The <code>svelte:head<\/code> component allows you to easily  insert elements into the <code>&lt;head&gt;<\/code> tag of the document, and since the Sveltekit pages are a document by itself, by using <code>svelte:head<\/code>, you\u2019ll end up defining the meta tags for each article in just one file \ud83d\ude00<\/p>\n<p>Now you have a few options to generate or retrieve the social image for your article. You can directly use the same image you added to the article and put it into the corresponding meta tag or use a neat trick provided by Cloudinary. Add overlays to the image so you can share more information in it.<\/p>\n<h2>With Cloudinary<\/h2>\n<p>If you decided to use Cloudinary, then you have options too:<\/p>\n<ul>\n<li>Upload the header image of the article to Cloudinary and apply transformations to it like adding text overlay, your logo etc<\/li>\n<li>or, use a pre-defined template with the corresponding text overlay for the article title.<\/li>\n<\/ul>\n<p>Personally, I prefer the second option, have a template image that shows my personal brand where I can include the post title and keywords or subtitle. To do this, you first need to define the template image.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.craft.do\/user\/full\/0a4fa0f6-6e08-58de-6dbc-6c56902ee35d\/doc\/0975E901-EF6C-4C06-87ED-0742402C1FF2\/85228054-0750-41C3-87E8-49776CA7AC31_2\/example_og_image.jpeg\" alt=\"example_og_image.jpeg\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1200\" height=\"627\"\/><\/p>\n<h3>What this template image should have?<\/h3>\n<p>To create a reusable template for your social image, need you need an image that can work for any post you make. This image will include<\/p>\n<ul>\n<li>Your logo or profile picture<\/li>\n<li>The post title: This is the dynamic part of the template, so in the template, you need to reserve space for it. There are some guidelines for the length of the title. You can use some tools like the <a href=\"https:\/\/app.sistrix.com\/en\/serp-snippet-generator\">SEO snippet generator<\/a> to check the size of your post titles<\/li>\n<li>Subtitle, keywords, or tagline: Just an additional text space to show some extra data.<\/li>\n<\/ul>\n<blockquote>\n<p>You can find a base template <a href=\"https:\/\/www.figma.com\/file\/MZjbFJW3vHNKhv9Cd1hDlp\/social-image-template?node-id=0%3A1\">in this Figma board<\/a><\/p>\n<\/blockquote>\n<blockquote>\n<p>Or just <a href=\"https:\/\/res.cloudinary.com\/matiasfha\/image\/upload\/v1624803419\/example_og_image.jpg\">use this image<\/a> for testing purposes<\/p>\n<\/blockquote>\n<p>With your base image done, you need to use Cloudinary transformation capabilities to add the corresponding text overlay. This can be simple done in a function to be reused across your project.<\/p>\n<p>Let\u2019s use the `Cloudinary nodejs package that can be found in npm<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install cloudinary --save\n\n<\/code><\/span><\/pre>\n<blockquote>\n<p>You can check in deep documentation in <a href=\"https:\/\/cloudinary.com\/documentation\/node_integration#overview\">cloudinary site<\/a><\/p>\n<\/blockquote>\n<p>For this function, you\u2019ll use the <code>cloudinary.url<\/code> method to define transformations using javascript objects and return the corresponding URL for it.<\/p>\n<p>Let\u2019s start by creating the function for this job. It will lib under <code>src\/lib<\/code> . SvelteKit comes with some default configurations for some folders like this one allowing you to access the modules from that library by using <code>$lib<\/code>.<\/p>\n<p>So, how this function looks like?<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n\ncloudinary.v2.config({\n\t<span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-keyword\">import<\/span>.meta.env.VITE_CLOUDINARY_CLOUD_NAME,\n});\n\n\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getOgImage<\/span>(<span class=\"hljs-params\">{ title, subTitle }<\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> url = cloudinary.v2.url(<span class=\"hljs-string\">'example_og_image.jpg'<\/span>, {\n\t\t<span class=\"hljs-attr\">transformation<\/span>: &#91;\n\t\t\t{ <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">1200<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">627<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span>, <span class=\"hljs-attr\">quality<\/span>: <span class=\"hljs-string\">'auto'<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">'auto'<\/span> },\n\t\t\t{\n\t\t\t\t<span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fit'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">700<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">x<\/span>: <span class=\"hljs-number\">480<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">y<\/span>: <span class=\"hljs-number\">254<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'south_west'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">color<\/span>: <span class=\"hljs-string\">'white'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">effect<\/span>: <span class=\"hljs-string\">'shadow:40'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">overlay<\/span>: {\n\t\t\t\t\t<span class=\"hljs-attr\">font_family<\/span>: <span class=\"hljs-string\">'roboto'<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">font_size<\/span>: <span class=\"hljs-number\">54<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">font_weight<\/span>: <span class=\"hljs-string\">'bold'<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-built_in\">encodeURIComponent<\/span>(title)\n\t\t\t\t}\n\t\t\t},\n\t\t\t{\n\t\t\t\t<span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fit'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">700<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">x<\/span>: <span class=\"hljs-number\">480<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">y<\/span>: <span class=\"hljs-number\">154<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">'south_west'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">color<\/span>: <span class=\"hljs-string\">'white'<\/span>,\n\t\t\t\t<span class=\"hljs-attr\">overlay<\/span>: {\n\t\t\t\t\t<span class=\"hljs-attr\">font_family<\/span>: <span class=\"hljs-string\">'roboto'<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">font_size<\/span>: <span class=\"hljs-number\">34<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">font_weight<\/span>: <span class=\"hljs-string\">'bold'<\/span>,\n\t\t\t\t\t<span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-built_in\">encodeURIComponent<\/span>(subTitle)\n\t\t\t\t}\n\t\t\t}\n\t\t]\n\t});\n\n\t<span class=\"hljs-keyword\">return<\/span> URL;\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> getOgImage;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s break down that code.<\/p>\n<p>First, you\u2019ll find the configuration for using cloudinary. This config method is using some environment variables. If you need to use custom environment variables inside your code, you need to name it with the prefix <code>VITE_<\/code>. The use of the prefix will make the variable immediately available under the <code>import.meta<\/code> object.<\/p>\n<blockquote>\n<p>Sveltekit uses Vite for building the application, and Vite uses dotenv to load the variables from the <code>.env<\/code> file.<\/p>\n<\/blockquote>\n<blockquote>\n<p>Vite enables the <code>import.meta<\/code> object to expose context-specific data to a module.<\/p>\n<\/blockquote>\n<blockquote>\n<p>You can find more information about environment variables in <a href=\"https:\/\/kit.svelte.dev\/faq#env-vars\">Sveltekit site<\/a> and <a href=\"https:\/\/vitejs.dev\/guide\/env-and-mode.html#modes\">Vite docs<\/a><\/p>\n<\/blockquote>\n<p>Then, you have the function definition that is receiving an object as an argument that has 2 attributes: <code>title<\/code> and <code>subTitle<\/code>. These are the overlays you want to create<\/p>\n<p>Now is the time to use cloudinary transformations.<\/p>\n<p>The <code>cloudinary.url<\/code> method accepts 2 arguments, the name of the base image and an object with options. This object is where the magic happens. One of the properties of the options object is the <code>transformation<\/code> array that accepts multiple transformation object descriptors. For this case, you\u2019ll use 3 transformations.<\/p>\n<ol>\n<li>First,  define the \u201ccanvas\u201d to work on. Will set format, quality, and size for the base image.<\/li>\n<li>Second will define the first text overlay for the title<\/li>\n<li>Third one defines the transformation to create another text overlay for the subTitle<\/li>\n<\/ol>\n<blockquote>\n<p>You can find more in deep documentation about transformation in <a href=\"https:\/\/cloudinary.com\/documentation\/node_image_manipulation#apply_common_image_transformations\">Cloudinary documentation site<\/a><\/p>\n<\/blockquote>\n<p>Now is time to use this function and really use your newly created social image.<\/p>\n<p>Let\u2019s go back to the <code>get<\/code> function in our endpoint route for an article <code>src\/routes\/api\/blog\/[uid].json.ts<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> Client <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/PrismicClient'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> PrismicDOM <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'prismic-dom'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> getOgImage <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/getOgImage'<\/span>;\n\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">get<\/span>(<span class=\"hljs-params\">{ params }<\/span>) <\/span>{\n\t<span class=\"hljs-keyword\">const<\/span> { uid } = params;\n\t<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> Client.getByUID(<span class=\"hljs-string\">'article'<\/span>, uid);\n\t<span class=\"hljs-keyword\">const<\/span> { title, subTitle } = response.data;\n\n\t<span class=\"hljs-keyword\">return<\/span> {\n\t\t<span class=\"hljs-attr\">body<\/span>: {\n\t\t\t...response.data,\n\t\t\t<span class=\"hljs-attr\">id<\/span>: response.id,\n\t\t\t<span class=\"hljs-attr\">href<\/span>: response.href,\n\t\t\t<span class=\"hljs-attr\">uid<\/span>: response.uid,\n\t\t\t<span class=\"hljs-attr\">ogImage<\/span>: getOgImage({\n\t\t\t\t<span class=\"hljs-attr\">text<\/span>: PrismicDOM.RichText.asText(title),\n\t\t\t\t<span class=\"hljs-attr\">subTitle<\/span>: PrismicDOM.RichText.asText(subTitle)\n\t\t\t})\n\t\t}\n\t};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here you can just use your <code>getOgImage<\/code> function passing down the text you want to show in the social image card, then the URL generated by the function will be available in the svelte component\/page found in <code>src\/routes\/blog\/[uid].svelte<\/code><\/p>\n<h3>Let\u2019s show the social image card<\/h3>\n<p>Now that the svelte component has the URL, we can just use <code>&lt;svelte:head&gt;<\/code> and add the corresponding meta tags, but it will be way better to have a reusable component for future pages, right?.<\/p>\n<p>Let\u2019s build an SEO component under <code>src\/lib\/components\/Seo.svelte<\/code><\/p>\n<p>And write down the meta tags we need for a basic Seo data definition.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> title: string;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> description: string = <span class=\"hljs-literal\">undefined<\/span>;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> keywords: string;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> canonical: string = <span class=\"hljs-literal\">undefined<\/span>;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> type: string;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> image: string;\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">svelte:head<\/span>&gt;<\/span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>{title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"robots\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"index, follow\"<\/span> \/&gt;<\/span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"googlebot\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"index,follow\"<\/span> \/&gt;<\/span>\n\t{#if description}\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{description}<\/span> \/&gt;<\/span>\n\t{\/if}\n\t{#if canonical}\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"canonial\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{canonical}<\/span> \/&gt;<\/span>\n\t{\/if}\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"keywords\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{keywords}<\/span> \/&gt;<\/span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">property<\/span>=<span class=\"hljs-string\">\"og:title\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{title}<\/span> \/&gt;<\/span>\n\t{#if description}\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">property<\/span>=<span class=\"hljs-string\">\"og:description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{description}<\/span> \/&gt;<\/span>\n\t{\/if}\n\t{#if canonical}\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">property<\/span>=<span class=\"hljs-string\">\"og:url\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{canonical}<\/span> \/&gt;<\/span>\n\t{\/if}\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">property<\/span>=<span class=\"hljs-string\">\"og:type\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{type<\/span> ? <span class=\"hljs-attr\">type<\/span> <span class=\"hljs-attr\">:<\/span> '<span class=\"hljs-attr\">site<\/span>'} \/&gt;<\/span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">property<\/span>=<span class=\"hljs-string\">\"og:image\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{image}<\/span> \/&gt;<\/span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"twitter:card\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"summary_large_image\"<\/span> \/&gt;<\/span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"twitter:title\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{title}<\/span> \/&gt;<\/span>\n\t{#if description}\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"twitter:description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{description}<\/span> \/&gt;<\/span>\n\t{\/if}\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"twitter:image\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">{image}<\/span> \/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">svelte:head<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This component exposes a few props. You can found them defined in the <code>&lt;script&gt;<\/code> tag as an exported variable.<\/p>\n<p>Then we have the component definition that creates some meta tags.\nThe ones that use the social image you created are <code>og:image<\/code> and <code>twitter:image<\/code><\/p>\n<p>Let\u2019s go back to the article page<code>src\/routes\/blog\/[uid].svelte<\/code> and use the new Seo component<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ts\"<\/span>&gt;<\/span><span class=\"javascript\">\n\t<span class=\"hljs-keyword\">import<\/span> PrismicDOM <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'prismic-dom'<\/span>;\n\t<span class=\"hljs-keyword\">import<\/span> Seo <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'$lib\/components\/Seo.svelte'<\/span>;\n\t<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> article;\n\t<span class=\"hljs-keyword\">const<\/span> title = PrismicDOM.RichText.asText(article.title);\n\t<span class=\"hljs-keyword\">const<\/span> subTitle = PrismicDOM.RichText.asText(article.subTitle);\n\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Seo<\/span> {<span class=\"hljs-attr\">title<\/span>} {<span class=\"hljs-attr\">subTitle<\/span>} <span class=\"hljs-attr\">keywords<\/span>=<span class=\"hljs-string\">\"\"<\/span> <span class=\"hljs-attr\">image<\/span>=<span class=\"hljs-string\">{article.ogImage}<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"article\"<\/span> \/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here the code import the Seo component from <code>$lib\/components\/Seo.svelte<\/code>, define the <code>article<\/code> prop with the data from the <code>load<\/code> function, and create a new variable with the <code>title<\/code> and <code>subtitle<\/code><\/p>\n<p>Then the code just uses the <code>Seo<\/code> component bypassing the required props. Note here that neat syntax <code>{title}<\/code> that is actually a shortcut to <code>title={title}<\/code>.<\/p>\n<p>Now, if you run your project and go to an article, you\u2019ll find the `og:image meta tag with the corresponding cloudinary URL of your social image.<\/p>\n<h2>Without Cloudinary<\/h2>\n<p>If you don\u2019t want to use Cloudinary transformation API and prefer to just use the same image you defined for your article, you can use the same Seo component. Still, instead of passing the <code>article.image<\/code> as value to <code>image<\/code> prop, you\u2019ll need to give the URL of the image. Since this project is using Prismic, you\u2019ll access <code>article.image.url<\/code><\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Seo<\/span> {<span class=\"hljs-attr\">title<\/span>} {<span class=\"hljs-attr\">subTitle<\/span>} <span class=\"hljs-attr\">keywords<\/span>=<span class=\"hljs-string\">\"\"<\/span> <span class=\"hljs-attr\">image<\/span>=<span class=\"hljs-string\">{article.image.url}<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"article\"<\/span> \/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h1>Summary<\/h1>\n<p>In this article, you were able to review the power of the new toolset for Svelte and create a basic website to publish your content using SvelteKit. At the same time, you learned the importance o<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,175,404,376,371],"class_list":["post-28273","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-guest-post","tag-image","tag-jamstack","tag-ssg-static-site","tag-svelte","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Setup a Developer Blog with Social Images using SvelteKit<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Setup a Developer Blog with Social Images using SvelteKit\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-07-27T18:08:04+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Setup a Developer Blog with Social Images using SvelteKit\",\"datePublished\":\"2021-07-27T18:08:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\"},\"wordCount\":9,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"keywords\":[\"Guest Post\",\"Image\",\"JAMStack\",\"SSG (Static Site)\",\"Svelte\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\",\"name\":\"Setup a Developer Blog with Social Images using SvelteKit\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"datePublished\":\"2021-07-27T18:08:04+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Setup a Developer Blog with Social Images using SvelteKit\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Setup a Developer Blog with Social Images using SvelteKit","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/","og_locale":"en_US","og_type":"article","og_title":"Setup a Developer Blog with Social Images using SvelteKit","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/","og_site_name":"Cloudinary Blog","article_published_time":"2021-07-27T18:08:04+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/"},"author":{"name":"","@id":""},"headline":"Setup a Developer Blog with Social Images using SvelteKit","datePublished":"2021-07-27T18:08:04+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/"},"wordCount":9,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"keywords":["Guest Post","Image","JAMStack","SSG (Static Site)","Svelte","Under Review"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/","name":"Setup a Developer Blog with Social Images using SvelteKit","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"datePublished":"2021-07-27T18:08:04+00:00","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/setup-a-developer-blog-with-social-images-using-sveltekit\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Setup a Developer Blog with Social Images using SvelteKit"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28273","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28273"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28273\/revisions"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28273"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28273"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28273"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}