Opens an external site in a new window
Pride Month Hold my hand 🫱🏾‍🫲🏼
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links
  • Profile
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links

Deno Fresh SVG Sprites: Optimized Icons 🔥 # Deno Fresh SVG Sprites: Optimized Icons 🔥 #

blurry low resolution placeholder image Deno Fresh Testing
  1. Home Rodney Lab Home
  2. Blog Posts Rodney Lab Blog Posts
  3. Deno Deno Blog Posts
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Deno Fresh SVG Sprites: Optimized Icons 🔥 #

Updated 2 months ago
8 minute read
Gunning Fog Index: 5.5
1 comment
Content by Rodney
blurry low resolution placeholder image Author Image: Rodney from Rodney Lab
SHARE:

📝 SVG Icon Optimization #

We will have a look at SVG Icon optimization, in this Deno Fresh SVG sprites post. Without care, SVG icons can take up half of the bytes of HTML code shipped for a particular page. This is especially true working in React and embedding the SVG inline, in React components.

In this post, we will see how you can create an SVG sprite sheet for all icons on a page, or a site. Loading that sheet as an independent, resource from an HTML use tag is a more performant alternative than adding the SVG content inline. We shall see, this approach also allows you easily to style the icons.

We see further optimizations, making the sprite sheet a static asset with Deno Fresh’s cache-busting feature. Finally, you get some example code for using the SVGO plugin in Deno to minify the sprite sheet. Hopefully, this is what you were looking for, so let’s get going. The code and approach is based on an excellent blog post by Ben Adam , which goes into the details of how it works together with limitations. We won’t revisit those aspects here.

🧱 What are we Building? #

blurry low resolution placeholder image Deno Fresh SVG Sprites: Screen capture shows a website with a Deno logo and a lemon icon.  The Deno icon depicts a black dinosaur head in profile with a rainy backdrop, in a cartoon style. The lemon is yellow, with two green leaves and a short stalk.
Deno Fresh SVG Sprites: Example

I put together a basic demo for the post, though you should be able just to copy and paste code snippets into a new or existing project, which you are already working on. There is a link to the full demo code further down.

Using these techniques, I reduced the size of shipped HTML by more than 25% (after compression) for a site I was working on.

🔎 Finding Icons #

If you are not new here, you probably already know that Icônes by Anthony Fu is my “go to” source of SVG icons. It is a huge library with dozens of icon collections, and you are unlikely to be unable to find any icon you are looking for there.

Although Icônes provides downloadable React components for each icon, here we need the raw SVG. To get going, go to the Icônes all-collection search page  to find and select an icon.

blurry low resolution placeholder image Deno Fresh SVG Sprites: Screen capture shows the Icôones website with the Simple Icons Deno icon selected.  The SVG snippet button is highlighted.
Deno Fresh SVG Sprites: Example

When you select your chosen icon, a pop-up will appear at the bottom of the window with the various format options. From Snippets, select SVG. This will copy the SVG code to your clipboard.

Example SVG Icon Code #

Here is the SVG code for the Simple Icons Deno icon (used in the example image above):

    
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12S5.373 0 12 0Zm-.469 6.793c-3.49 0-6.204 2.196-6.204 4.928c0 2.58 2.498 4.228 6.37 4.145l.118-.003l.425-.012l-.109.279l.013.029c.031.072.06.145.084.22l.01.028l.015.045l.021.065l.014.045l.014.047l.015.049l.021.075l.022.079l.015.054l.023.084l.022.088l.023.091l.023.095l.015.065l.024.1l.023.103l.032.143l.017.074l.024.114l.024.117l.025.12l.035.174l.029.142l.037.195l.02.1l.028.155l.03.158l.039.217l.04.225l.04.231l.041.24l.042.246l.042.254l.042.26l.032.201l.055.344l.022.14l.055.36l.045.295l.034.227l.046.308l.023.156a10.758 10.758 0 0 0 6.529-3.412l.05-.055l-.238-.891l-.633-2.37l-.395-1.47l-.348-1.296l-.213-.787l-.136-.498l-.081-.297l-.073-.264l-.032-.11l-.018-.064l-.01-.034l-.008-.026a6.042 6.042 0 0 0-2.038-2.97c-1.134-.887-2.573-1.351-4.252-1.351ZM8.467 19.3a.586.586 0 0 0-.714.4l-.004.013l-.527 1.953c.328.163.665.309 1.008.437l.08.03l.57-2.114l.004-.015a.586.586 0 0 0-.417-.704Zm3.264-1.43a.586.586 0 0 0-.715.4l-.004.014l-.796 2.953l-.004.014a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.003-.014a.585.585 0 0 0 .013-.067l.002-.022l-.019-.096l-.027-.138l-.018-.086a.584.584 0 0 0-.367-.295Zm-5.553-3.04a.59.59 0 0 0-.037.09l-.005.02l-.797 2.953l-.004.014a.586.586 0 0 0 1.131.306l.004-.014l.723-2.678a5.295 5.295 0 0 1-1.015-.692Zm-1.9-3.397a.586.586 0 0 0-.715.4l-.004.013l-.797 2.953l-.003.015a.586.586 0 0 0 1.13.305l.005-.014l.797-2.953l.003-.015a.586.586 0 0 0-.416-.704Zm17.868-.67a.586.586 0 0 0-.715.399l-.004.014l-.797 2.953l-.003.014a.586.586 0 0 0 1.13.305l.005-.014l.797-2.953l.003-.014a.586.586 0 0 0-.416-.704ZM2.542 6.82a10.707 10.707 0 0 0-1.251 3.926a.586.586 0 0 0 1.002-.22l.004-.014l.797-2.953l.003-.014a.586.586 0 0 0-.555-.725Zm17.585.02a.586.586 0 0 0-.714.4l-.004.014l-.797 2.953l-.004.014a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.004-.014a.586.586 0 0 0-.417-.704Zm-7.846 1.926a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5Zm-6.27-4.733a.586.586 0 0 0-.715.398l-.004.015l-.797 2.953l-.004.014a.586.586 0 0 0 1.132.305l.003-.014l.797-2.953l.004-.014a.586.586 0 0 0-.417-.704Zm10.238.558a.586.586 0 0 0-.714.399l-.004.014l-.536 1.984c.347.171.678.373.99.603l.051.038l.626-2.32l.004-.014a.586.586 0 0 0-.417-.704Zm-5.211-3.33a10.76 10.76 0 0 0-1.115.158l-.078.015l-.742 2.753l-.004.015a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.004-.015a.583.583 0 0 0 .003-.264Zm7.332 2.04l-.156.58l-.004.015a.586.586 0 0 0 1.131.305l.004-.014l.017-.063a10.838 10.838 0 0 0-.923-.772l-.069-.051Zm-4.636-1.944l-.283 1.048l-.003.014a.586.586 0 0 0 1.13.305l.005-.014l.297-1.102c-.35-.097-.705-.176-1.063-.237l-.083-.014Z"
/>
</svg>

You can see the full SVG for just one, simple icon is already a fair few bytes! In the next section, we start building the component which pulls in the path from a sprite sheet, rather than inlining it.

🧩 Creating an SVG Component with use Element #

Here is the code for a Preact component, which will produce the SVG icon above. The file path in the demo code is components/Icons/Deno.tsx:

components/Icons/Deno.tsx
tsx
    
1 import { asset } from "$fresh/runtime.ts";
2 import { FunctionComponent } from "preact";
3
4 interface DenoIconProps {
5 colour?: string;
6 height?: number;
7 width: number;
8 }
9
10 export const DenoIcon: FunctionComponent<DenoIconProps> = function DenoIcon({
11 colour = "inherit",
12 width,
13 height = width,
14 }) {
15 // SVG path by Simple Icons (https://simpleicons.org/) via Icônes (https://icones.js.org/collection/all)
16
17 return (
18 <svg
19 width={`${width}px`}
20 height={`${height}px`}
21 style={{ color: colour }}
22 >
23 <use href={`${asset("/sprite.svg")}#deno`} />
24 </svg>
25 );
26 };

Following Ben’s method, the Preact component is an HTML use tag sandwiched inside an SVG tag. We can include styles on the SVG tag, and the path will go into the sprite sheet. The final sprite sheet will be a static asset in the static directory of the project. deno id in the href path is our own identifier, used to pick the right icon from the sprite sheet.

The Deno Fresh cache-busting comes from wrapping our path in the asset function (imported from $fresh/runtime in line 1). Deno will add a hash to the end of the path in the output site. This lets us set a long cache time, since the path will change whenever the sprite sheet changes, and the new sheet will be fetched, rather than recycling any cached version.

🥤 Adding the SVG Icon to the Sprite Sheet #

The base sprite template will look like this (file path is assets/sprite.svg in the demo code):

assets/sprite.svg
svg
    
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<defs>
</defs>
</svg>

We will add each icon as a symbol element, with id inside the defs element. In the next section, we optimize this file and output the optimized version to static/sprite.svg. Here is the completed version with a couple of icons:

assets/sprite.svg
svg
    
1 <svg
2 xmlns="http://www.w3.org/2000/svg"
3 xmlns:xlink="http://www.w3.org/1999/xlink"
4 >
5 <defs>
6 <symbol viewBox="0 0 24 24" id="deno">
7 <path
8 fill="currentColor"
9 d="M12 0c6.627 0 12 5.373 12 12s-5.373 12-12 12S0 18.627 0 12S5.373 0 12 0Zm-.469 6.793c-3.49 0-6.204 2.196-6.204 4.928c0 2.58 2.498 4.228 6.37 4.145l.118-.003l.425-.012l-.109.279l.013.029c.031.072.06.145.084.22l.01.028l.015.045l.021.065l.014.045l.014.047l.015.049l.021.075l.022.079l.015.054l.023.084l.022.088l.023.091l.023.095l.015.065l.024.1l.023.103l.032.143l.017.074l.024.114l.024.117l.025.12l.035.174l.029.142l.037.195l.02.1l.028.155l.03.158l.039.217l.04.225l.04.231l.041.24l.042.246l.042.254l.042.26l.032.201l.055.344l.022.14l.055.36l.045.295l.034.227l.046.308l.023.156a10.758 10.758 0 0 0 6.529-3.412l.05-.055l-.238-.891l-.633-2.37l-.395-1.47l-.348-1.296l-.213-.787l-.136-.498l-.081-.297l-.073-.264l-.032-.11l-.018-.064l-.01-.034l-.008-.026a6.042 6.042 0 0 0-2.038-2.97c-1.134-.887-2.573-1.351-4.252-1.351ZM8.467 19.3a.586.586 0 0 0-.714.4l-.004.013l-.527 1.953c.328.163.665.309 1.008.437l.08.03l.57-2.114l.004-.015a.586.586 0 0 0-.417-.704Zm3.264-1.43a.586.586 0 0 0-.715.4l-.004.014l-.796 2.953l-.004.014a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.003-.014a.585.585 0 0 0 .013-.067l.002-.022l-.019-.096l-.027-.138l-.018-.086a.584.584 0 0 0-.367-.295Zm-5.553-3.04a.59.59 0 0 0-.037.09l-.005.02l-.797 2.953l-.004.014a.586.586 0 0 0 1.131.306l.004-.014l.723-2.678a5.295 5.295 0 0 1-1.015-.692Zm-1.9-3.397a.586.586 0 0 0-.715.4l-.004.013l-.797 2.953l-.003.015a.586.586 0 0 0 1.13.305l.005-.014l.797-2.953l.003-.015a.586.586 0 0 0-.416-.704Zm17.868-.67a.586.586 0 0 0-.715.399l-.004.014l-.797 2.953l-.003.014a.586.586 0 0 0 1.13.305l.005-.014l.797-2.953l.003-.014a.586.586 0 0 0-.416-.704ZM2.542 6.82a10.707 10.707 0 0 0-1.251 3.926a.586.586 0 0 0 1.002-.22l.004-.014l.797-2.953l.003-.014a.586.586 0 0 0-.555-.725Zm17.585.02a.586.586 0 0 0-.714.4l-.004.014l-.797 2.953l-.004.014a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.004-.014a.586.586 0 0 0-.417-.704Zm-7.846 1.926a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5Zm-6.27-4.733a.586.586 0 0 0-.715.398l-.004.015l-.797 2.953l-.004.014a.586.586 0 0 0 1.132.305l.003-.014l.797-2.953l.004-.014a.586.586 0 0 0-.417-.704Zm10.238.558a.586.586 0 0 0-.714.399l-.004.014l-.536 1.984c.347.171.678.373.99.603l.051.038l.626-2.32l.004-.014a.586.586 0 0 0-.417-.704Zm-5.211-3.33a10.76 10.76 0 0 0-1.115.158l-.078.015l-.742 2.753l-.004.015a.586.586 0 0 0 1.131.305l.004-.014l.797-2.953l.004-.015a.583.583 0 0 0 .003-.264Zm7.332 2.04l-.156.58l-.004.015a.586.586 0 0 0 1.131.305l.004-.014l.017-.063a10.838 10.838 0 0 0-.923-.772l-.069-.051Zm-4.636-1.944l-.283 1.048l-.003.014a.586.586 0 0 0 1.13.305l.005-.014l.297-1.102c-.35-.097-.705-.176-1.063-.237l-.083-.014Z"
10 />
11 </symbol>
12 <symbol viewBox="0 0 512 512" id="lemon">
13 <!-- TRUNCATED -->
14 </symbol>
15 </defs>
16 </svg>

Notice:

  • in lines 6 and 14, we use the viewBox values from the source SVG;
  • we place the path elements inside the symbol tag; and
  • we must remember to add an id matching the id given in the corresponding component.

If your SVG has a defs tag, just include the icon’s defs tag contents above the symbol element for your icon.

🔥 Optimizing the Sprite #

As a final step, you can add a Deno script to optimize the static, served sprite. First, add the svg package to the project for optimising the spritesheet:

    
deno add npm:svgo

Then, update deno.json:

deno.json
json
    
1 {
2 "lock": false,
3 "tasks": {
4 "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
5 "start": "deno run -A --watch=static/,routes/ dev.ts",
6 "update": "deno run -A -r https://fresh.deno.dev/update .",
7 "build": "deno run -A dev.ts build",
8 "preview": "deno run -A main.ts",
9 "minify": "deno run --allow-env=DENO_ENV --allow-read --allow-run --allow-write minify-svg-sprite.ts"
10 },
11 "fmt": {
12 "exclude": ["static/*.css"]
13 },
14 "lint": {
15 "rules": {
16 "tags": [
17 "fresh",
18 "recommended"
19 ]
20 }
21 },
22 "exclude": [
23 "**/_fresh/*"
24 ],
25 "imports": {
26 "$fresh/": "https://deno.land/x/fresh@1.7.3/",
27 "@/": "./",
28 "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
29 "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
30 "@std/dotenv": "jsr:@std/dotenv@^0.225.3",
31 "preact": "https://esm.sh/preact@10.22.0",
32 "preact/": "https://esm.sh/preact@10.22.0/",
33 "stylelint": "npm:stylelint@^16.18.0",
34 "svgo": "npm:svgo@^3.3.2"
35 },
36 "compilerOptions": {
37 "jsx": "react-jsx",
38 "jsxImportSource": "preact"
39 }
40 }

Now, create the script minify-svg-sprite.ts in the project root directory:

minify-svg-sprite.ts
typescript
    
1 import { optimize } from "svgo";
2
3 const svgString = await Deno.readTextFile("./assets/sprite.svg");
4 const { data: optimisedSVG } = optimize(svgString, {
5 path: "assets/sprite.svg",
6 plugins: [
7 {
8 name: "preset-default",
9 params: {
10 overrides: {
11 removeHiddenElems: false,
12 },
13 },
14 },
15 ],
16 });
17 await Deno.writeTextFile("./static/sprite.svg", `${optimisedSVG}\n`);
18
19 Deno.exit(0);

The plugins config here is important. Without it, SVGO will optimize away the SVG needed for your icons.

You can now use the SVG icon components on your site pages:

routes/index.tsx
tsx
    
1 import { DenoIcon } from "@/components/Icons/Deno.tsx";
2 import { LemonIcon } from "@/components/Icons/Lemon.tsx";
3
4 export default function Home() {
5 return (
6 <main>
7 <DenoIcon colour="#fff" width={192} />
8 <LemonIcon width={192} />
9 </main>
10 );
11 }

🗳 Poll #

Did you already know about the Deno Fresh cache busting feature?
Voting reveals latest results.

💯 Deno Fresh SVG Sprites: Checking Your Work #

Run the minify script, and check it outputs a minified version of the sprite to static/sprite.svg. Remember to re-run this script whenever you add new icons to the sprite.

If you pull open dev tools and have a look at the Network tab, you should see the sprite is downloaded. The path should have a hash on the end of it (something like sprite.svg?__frsh_c=8d89...).

blurry low resolution placeholder image Screen capture show the Network tab in Firefox Developer Tools.  A GET request for the SVG sprite sheet is highlighted, show the sprite.svg url, with a hash appended as a query parameter.  The auxialiary pane, to the right shows an empty white rectangle and meta below states the SVG dimensions are 0 x 0.
Testing: Dev Tools Network

Jumping onto the Inspector tab, use should find a use element, instead of the inline SVG code for the icon (though you can expand the enclosed #shadow-root to inspect the SVG included from the sprite).

blurry low resolution placeholder image Screen capture show the Inspector tab in Firefox Developer Tools. An S V G tag is highlighted in the main window.  In Inspector itself, within the s v g tag is a use tag, rather than the inline S V G.  The h ref on the use tag is set to /sprite.svg, with a hash appended as a query parameter.
Testing: Dev Tools Inspector

🙌🏽 Deno Fresh SVG Sprites: Wrapping Up #

We saw how you can set up Deno Fresh SVG Sprites in your own project. In particular, we saw:

  • how you can use Icônes to find SVG icons;
  • how you make sure create an SVG sprite sheet for use with Preact components; and
  • a way to optimize SVG assets in Deno.

The complete code for this post . I do hope the post has either helped you with an existing project or provided some inspiration for getting started with setting up a new one.

Get in touch if you have some questions about this content or suggestions for improvements. You can add a comment below if you prefer. Also, reach out with ideas for fresh content on Deno or other topics!

🏁 Deno Fresh SVG Sprites: Summary #

What is a performant way to add SVG icons in Deno Fresh? #

Creating an SVG sprite sheet is an efficient way to add SVG icons to your Deno Fresh site. Using this approach, you can also style the icons, changing colours or sizes, for example. We saw you can create an SVG sprite sheet as a static asset. That sprite sheet will include the SVG icons, where each icon has an id. In your icon component, you will add a `use` element, in place of the inline SVG path, setting the use tag href attribute to the sprite sheet URL (with the id tacked onto the end).

How does Deno cache-busting work? #

To cache bust static assets in Deno, first create a special URL with the asset function. You can import it as a named import from `$fresh/runtime` and it takes a single argument of the path of the asset (which will already be in the `static` folder of your project). When Deno generates the site, it computes a hash of the static assets and adds this as a query parameter on the URL for the asset. You can now set a long expiry for caching the asset. As the URL includes the hash, whenever the asset contents change, the URL also changes, meaning the new asset has to be downloaded, forsaking the cached version.

Does SVGO work with Deno? #

Yes, you can include it in your import map as `https://esm.sh/svgo@3.0.2/`, for example. From there you can use it in any SVG minifying scripts you have.

🙏🏽 Deno Fresh SVG Sprites: Feedback #

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, then please consider supporting me through Buy me a Coffee.

blurry low resolution placeholder image ask Rodney X (formerly Twitter) avatar

Rodney

@askRodney

Just dropped a new post on adding SVG logo sprites in 🍋 Deno Fresh, which:

- lets you style icons in Preact,

- reduces shipped HTML vs. adding inline SVG

We also look at minifying and cache-busting the sprites.

Hope you find it useful!

#learndenohttps://t.co/td5EA5nuya

— Rodney (@askRodney) July 12, 2023

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter, @rodney@toot.community  on Mastodon and also the #rodney  Element Matrix room. Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as Deno. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Thanks for reading this post. I hope you found it valuable. Please get in touch with your feedback and suggestions for posts you would like to see. Read more about me …

blurry low resolution placeholder image Rodney from Rodney Lab
TAGS:
DENO

Likes:

Likes

  • Heiker profile avatar
Likes provided by Mastodon & X via Webmentions.

Related Post

blurry low resolution placeholder image Trying out Deno Fresh: new Fast Framework for Web

Trying out Deno Fresh: new Fast Framework for Web

deno
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Leave a comment …

Your information will be handled in line with our Privacy Policy .

Comments

  • Suresh

    Is it working in Firefox, if so can you please give me a demo link. Thanks in Advance.

    2 years ago

Ask for more

1 Nov 2022 — Astro Server-Side Rendering: Edge Search Site
3 Oct 2022 — Svelte eCommerce Site: SvelteKit Snipcart Storefront
1 Sept 2022 — Get Started with SvelteKit Headless WordPress

Copyright © 2020 – 2025 Rodney Johnson. All Rights Reserved. Please read important copyright and intellectual property information.

  • Home
  • Profile
  • Plus +
  • Newsletter
  • Contact
  • Links
  • Terms of Use
  • Privacy Policy
We use cookies  to enhance visitors’ experience. Please click the “Options” button to make your choice.  Learn more here.