tvanc
tvanc

Reputation: 4324

SvelteKit: SVG on HTML page, or as standalone SVG file depending on route

I have a component, image.svelte which renders an SVG file. On one route, say /image, I want a page that includes that component rendered within an HTML document. But if you go to /image.svg, I want just the SVG document by itself and I want the page to be served with Content-Type: image/svg+xml.

I started by making creating a file like src/routes/image[ext]/+page.svelte, but there doesn't seem to be a way to break out of src/app.html.

Next I tried making two files, src/routes/image/+page.svelte and src/routes/image.svg/+server.js, but it doesn't seem to be possible to render components directly from a server-side script in SvelteKit.

So I'm at a loss and haven't find what I'm looking for in the docs.

Upvotes: 1

Views: 656

Answers (2)

baisong
baisong

Reputation: 60428

I wanted to do something similar, and I found that the Svelte 5 API is a little different. Now, this is working for me:

/src/lib/image/image.svelte

<script>
  let items = $state(["one", "two", "three"]);
</script>

<svg
  version="1.1"
  viewBox="0 0 200 200"
  xmlns="http://www.w3.org/2000/svg"
>
  <g>
    {#each items as item, i}
      <text x={i * 10} y={(i + 1) * 20}>{item}</text>
    {/each}
  </g>
</svg>

<style>
</style>

/src/routes/mysvg/+server.js (no +page.svelte necessary)

import { render } from 'svelte/server';
import image from "$lib/image/image.svelte";

/** @type {import('./$types').RequestHandler} */
export function GET() {
  const { html } = render(image);

  return new Response(html, {
    headers: {
      "Content-Type": "image/svg+xml",
      "Content-Disposition": "inline;filename=image.svg",
    },
  });
}

Here's more about the API changes: Components are no longer classes

Upvotes: 0

tvanc
tvanc

Reputation: 4324

Turned out I was pretty close to the answer. I just needed to explore the svelte module a little.

I accomplished what I wanted to do by creating one file, src/routes/image/+page.svelte, for serving the HTML page with the SVG in it; and by creating a second file, src/routes/image.svg/+server.js

I pieced together from error messages and old documentation that importing svelte/register no longer worked and I needed to import svelte/ssr instead, and that svelte components have a .render() method which returns an object with html, css, and head properties. In my case I only needed the html property.

This was the final result

import "svelte/ssr"
import Image from "$lib/Image/index.svelte"

export function GET() {
    const {html} = Image.render({svg: true})

    return new Response(html, {
        headers: {
            "Content-Type": "image/svg+xml",
            "Content-Disposition": "inline;filename=image.svg"
        }
    })
}

Upvotes: 3

Related Questions