Static sites & CMS
Building with a static-site generator or a CMS? Use a pre-rendered SVG: the build tool or
CMS inlines the file and the animation is on screen before any JavaScript runs. Even the
flavours with a <script> just work when inlined. Where a framework also runs client code,
the JSON format with a player is available too.
Static site generators
Section titled “Static site generators”| Framework | Inline a pre-rendered SVG | JSON alternative |
|---|---|---|
| Astro | import svg from './animation.svg?raw'; then <Fragment set:html={svg} /> | use the React or Vue component with the client:load directive, which tells Astro to run it in the browser (an “island”, in Astro’s own terms) |
| Gatsby | gatsby-plugin-react-svg (CSS flavour) or dangerouslySetInnerHTML with the raw file | React component |
| Jekyll | {% include_relative assets/animation.svg %} | use the player library @pixodesk/svg-animator-web: it ships a single .js file (the “UMD” build) that you copy to your site and load with a plain <script> tag — how to get the file, example below |
| Hugo | {{ readFile "static/animation.svg" | safeHTML }} | use the player library @pixodesk/svg-animator-web: it ships a single .js file (the “UMD” build) that you copy to your site and load with a plain <script> tag — how to get the file, example below |
| 11ty (Eleventy) | {% include "animation.svg" %} | use the player library @pixodesk/svg-animator-web: it ships a single .js file (the “UMD” build) that you copy to your site and load with a plain <script> tag — how to get the file, example below |
| Docusaurus / MDX | import the CSS flavour as a component (SVGR is built in) — example below | React component |
The rows above use two different ways of putting the file into the page, and they are not
equal. A raw include — Astro’s ?raw import, Jekyll’s include, Hugo’s readFile —
copies the file’s content into the HTML exactly as it is, <script> tags included; that is
why the JS-triggers and JS-animation flavours work in these generators. A component
import (SVGR, vite-svg-loader) instead converts the SVG into a component, and removes any
<script> along the way — so it works for the CSS flavour only.
Docusaurus / MDX
Section titled “Docusaurus / MDX”Docusaurus imports .svg files as React components through SVGR, so a CSS-flavour export drops
into any .mdx page like an icon, right among the page’s ordinary Markdown content:
import Ball from './animation.svg';
# Welcome
<Ball style={{ width: 300, height: 300 }} />(# Welcome is just the page’s own Markdown heading — in .mdx, Markdown text and imported
components sit side by side.)
An On load export plays on its own. For a hover, click or scroll trigger, wrap it in the
React package’s PixodeskSvgCssAnimator, which toggles the play classes for you:
import Ball from './animation.svg';import { PixodeskSvgCssAnimator } from '@pixodesk/svg-animator-react';
<PixodeskSvgCssAnimator start="mouseOver" mouseOut="pause" style={{ width: 300, height: 300 }}> <Ball /></PixodeskSvgCssAnimator>One setting matters. Docusaurus runs SVGO, an SVG optimiser, on every imported SVG — and
one of SVGO’s optimisations (inlineStyles) rewrites the file’s <style> rules as plain
attributes on the elements. That rewrite destroys the animation’s on/off switch: the file’s
CSS rules only apply while the play classes are on the root <svg>
(how that works), and once
the rules are inlined, the classes no longer control anything — the animation cannot be
started or stopped. Turn SVGO off for the SVGR plugin in docusaurus.config.js:
export default { presets: [ ['classic', { svgr: { svgrConfig: { svgo: false } }, }], ],};Pasting the SVG markup straight into an .mdx file does not work: MDX reads the { } inside
<style> as expressions and drops <script>. For the scripted flavours, import the file raw
and set it as HTML — Docusaurus renders the page to static HTML at build time, so the file’s
own <script> runs when the page loads:
import svg from '!!raw-loader!./animation.svg';
<div dangerouslySetInnerHTML={{ __html: svg }} />The !!raw-loader! prefix in the import needs the raw-loader package — add it with
npm install raw-loader. This is not our invention: it is the approach Docusaurus itself
recommends for bringing a file’s raw text into a page.
Application frameworks that render on the client — Next.js, Nuxt, SvelteKit, Angular — are a different case: there the JSON format with a player is the natural fit. See React → Next.js and Vue → Nuxt.
Vanilla JavaScript on any static page
Section titled “Vanilla JavaScript on any static page”Example:
static/vanilla-umd—pnpm example:docs, then open#static/vanilla-umd.
A page with no framework and no build step: the UMD build copied to your site, one element per animation, one call:
<div data-px-animation-src="/bouncing-ball.json"></div><script src="/js/pixodesk-svg-animator.umd.min.js"></script><script>PixodeskAnimator.loadTagAnimators();</script>The pixodesk-svg-animator.umd.min.js file in the first <script> tag is the player itself —
one .js file you copy out of the npm package and put on your own site;
Installing the players (overview)
shows exactly how. Everything you can do with the player from here — controlling playback,
reacting to events — is described in Web player.
CMS and website builders
Section titled “CMS and website builders”Paste the SVG’s markup into the platform’s HTML / code block. One thing to know first: most
of these platforms clean up any HTML you paste before publishing it — they strip out tags
they consider risky, and <script> is usually the first to go. When that happens to a
pre-rendered SVG of a flavour that carries a script (JS triggers, JS animation), the file
still shows, but its animation is broken or cannot start. The SVG + CSS animation flavour
has no <script> at all, so nothing can be stripped from it — it is the safe choice on any
platform that filters pasted HTML.
| Platform | Method |
|---|---|
| WordPress | a Custom HTML block, or in a theme: <?php echo file_get_contents(get_template_directory() . '/assets/animation.svg'); ?> |
| Shopify | save the SVG file as a theme snippet (e.g. snippets/animation.liquid), then put {% render 'animation' %} in the template where it should appear — or simply paste the SVG markup into a section’s custom Liquid code |
| Webflow | Embed component → paste the SVG markup |
| Squarespace | Code block → paste the SVG markup |
| Wix | Embed HTML element → paste the SVG markup (runs in an iframe) |
| Framer / Notion / others | an embed / code block that accepts raw HTML |
Before you publish — several things to check
Section titled “Before you publish — several things to check”- Content Security Policy (CSP). Inlined scripts (JS-triggers / JS-animation flavours) count as inline scripts; if your site’s CSP forbids them, use the CSS flavour, or JSON with the player loaded from your own origin.
- ⚠️ Don’t inline the same file twice on one page. Different files are fine; a duplicate of the same file repeats its ids and breaks masks, gradients and bindings. For the same animation in two places, export a separate file for each (every export gets its own ids) or use JSON, where the player gives every copy fresh ids (read more).
- Sizing. Keep the
viewBox, remove fixedwidth/heightif you want the SVG to scale with its container, and size the container with CSS. - Animations further down the page. An animation the visitor has to scroll to should wait until it is on screen, or it plays while nobody sees it and is over by the time the visitor gets there. Keep the default trigger — On load with Off Screen: pause — and export as SVG + CSS animation + JS triggers (or JSON). The pure SVG + CSS flavour cannot see scrolling, so it starts as soon as the page loads.