# Svelte and SvelteKit

> How midcode runs SvelteKit and Vite + Svelte, how it reads .svelte files with your own compiler, and which edits it writes into them.

- Page: https://midcode.app/docs/frameworks/svelte
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt

midcode edits SvelteKit and Vite + Svelte projects in their `.svelte` files: text, static classes, attributes and tags, plus inserting, moving, duplicating and removing elements. It reads each file with the Svelte compiler your project already has, so what midcode sees is what Svelte sees.

Components are recognised, their props show as controls, and a component opens on its own canvas with variants and states. The free canvas comes with the next release, for plain elements. [variables](https://midcode.app/docs/editor/variables.md), new pages (SvelteKit) and Site settings come with the next release.

## At a glance

| | Svelte and SvelteKit |
| --- | --- |
| Detected by | `@sveltejs/kit` in `package.json` (SvelteKit), or `vite` together with `svelte` (Vite + Svelte) |
| Runs with | `vite dev`, with a config of midcode's that loads yours and puts one plugin first |
| Elements are marked by | That plugin, before Svelte compiles each `.svelte` file, using your project's `svelte/compiler` |
| Editing | Text, static classes, attributes, tags; insert, move, duplicate, remove |
| Components | Instances, props, variants and states. Variables in the next release |
| Pages | SvelteKit: every `+page` under `src/routes`, and new ones in the next release. Vite + Svelte: none listed |
| Styles | Tailwind 4 classes, or `mid:` classes with `midcode.css` imported in `src/routes/+layout.svelte` |
| Tried with | A SvelteKit project made by `sv create`, with Tailwind 4 |

## How midcode runs it

midcode reads `package.json`. `@sveltejs/kit` makes the project SvelteKit. Without it, `vite` with `svelte` is a Vite + Svelte project.

SvelteKit is a Vite plugin, so both are started the same way: your project's own `vite`, run by the `node` of your login shell (midcode's own Node if the Mac has none), with a config of midcode's in front of yours.

```bash
vite dev /Users/you/sites/docs \
  --config "/Users/you/Library/Application Support/midcode/injected/vite.config.mjs" \
  --port 4310 --strictPort --host localhost
```

That config lives in midcode's own data folder. It loads your `vite.config` with your own Vite and returns it with one more plugin, placed first so it reads each `.svelte` file before Svelte's plugin compiles it. Neither `vite.config` nor `svelte.config.js` is touched, nothing is installed, and the plugin only exists in the dev server midcode started. The file itself, and what happens when Vite doesn't start this way (midcode runs your own `dev` script, and the site shows without marks), are in [React with Vite](https://midcode.app/docs/frameworks/react-vite.md).

### What the plugin adds

The plugin parses each `.svelte` file with your project's `svelte/compiler` and adds attributes to what Svelte is given. The file on disk is not changed, and lines stay where they are.

```svelte title="src/routes/+page.svelte"
<script>
  import Faq from '$lib/Faq.svelte'
</script>

<main class="mx-auto max-w-3xl px-6 py-24">
  <h1 class="text-5xl font-semibold">Selected work</h1>
  <Faq title="Questions" />
</main>
```

```svelte title="src/lib/Faq.svelte"
<script>
  let { title } = $props()
</script>

<section class="rounded-2xl p-6">
  <h2>{title}</h2>
</section>
```

In the page:

```html
<main data-mc="src/routes/+page.svelte:5:1" class="mx-auto max-w-3xl px-6 py-24">
  <h1 data-mc="src/routes/+page.svelte:6:3" class="text-5xl font-semibold">Selected work</h1>
  <section data-mc="src/lib/Faq.svelte:5:1" data-mcu="Faq|src/routes/+page.svelte:7:3" class="rounded-2xl p-6">
    <h2 data-mc="src/lib/Faq.svelte:6:3">Questions</h2>
  </section>
</main>
```

- `data-mc` goes on every element: the file, the line and the column of its `<`.
- `data-mci` (`Faq|src/routes/+page.svelte:7:3`) is handed to each component instance as a prop.
- `data-mcu` goes on the elements at the top of a component, and says where the component is used. midcode reads it from the component's `$props()` (or `$$props` in a component that uses `export let`). That's how [Layers](https://midcode.app/docs/editor/layers.md) names components and how a section that is a component moves where it's used.

`<svelte:head>`, `<svelte:window>`, `<svelte:body>`, `<svelte:document>` and `<svelte:options>` are left alone. While a file doesn't parse (you're halfway through typing), it gets no marks: Svelte shows its error and midcode steps aside.

## What you can edit

Select the `<h1>`, change its size, and one class is swapped:

```diff title="src/routes/+page.svelte"
-  <h1 class="text-5xl font-semibold">Selected work</h1>
+  <h1 class="text-6xl font-semibold">Selected work</h1>
```

- **Text.** Double-click an element that holds only text and type. A `{`, `}` or `<` you type is written as an entity, so it can't start an expression or a tag. When the text is an expression (`{title}`), midcode looks for the exact words in the project and edits them where they're written, here the `title="Questions"` in `+page.svelte`. See [Text, images and video](https://midcode.app/docs/editor/text-and-media.md).
- **Classes.** The written part of `class="…"`. With an expression inside (`class="card {active ? 'on' : ''}"`), midcode edits the text around it and leaves the expression alone.
- **Attributes and tags.** `src`, `alt`, `href` and the rest when they're written as text, and changing one HTML tag for another. New images go to `static/` in SvelteKit, `public/` in Vite + Svelte.
- **Insert.** What you drag in from [Insert](https://midcode.app/docs/editor/insert.md) is converted to Svelte markup before it's written: `class`, `for`, no self-closing `<div />`. An import it needs goes into the component's `<script>` (one is added at the end of the file when there's none), as `$lib/…` for files in `src/lib`. The result is parsed with your compiler first. If it wouldn't parse, nothing is written.
- **Move, duplicate, remove.** An element moves within its file, and the comment above it travels with it. See [Select, move and resize](https://midcode.app/docs/editor/select-move-resize.md).
- **Props.** Select an instance and the right panel shows what the component declares, from `let { title, count = 3 }: Props = $props()` or from `export let`, as typed controls. Setting one writes the attribute on the instance.
- **Components.** A `.svelte` file with a capitalised name is listed in Assets, and opens on its own canvas. A variant is written as a `variant` prop with its default, `data-variant={variant}` and a `group/<name>` class on each top-level element. States are `hover:`, `active:` and `focus-visible:` classes. See [Components](https://midcode.app/docs/editor/components.md).

[CMS](https://midcode.app/docs/data/cms.md), [Languages](https://midcode.app/docs/data/languages.md), [Code view](https://midcode.app/docs/editor/code.md) and [Publish](https://midcode.app/docs/publish/publish.md) work as in any project.

### Several elements at once (next release)

Removing several selected elements at once, wrapping elements in a stack (`⇧A`) and moving several together work in `.svelte` files in the next release. In the released version those three are JSX only: in a `.svelte` file you remove and move one element at a time. They were tried in `.vue` files, which go through the same code as `.svelte` files.

`⌥`-drag comes to `.svelte` files in the same release: the element stays where it is, and a copy goes beside or inside the element you drop it on, in the same file. The released version answers "Copies made by dragging aren't available in Svelte files yet." A copy can't float on the empty canvas: that's the free canvas, which is Next.js only.

## Pages

In SvelteKit, every folder of `src/routes` with a `+page.svelte` (or `+page.ts`, `.js`, `.md`) is a page. `(group)` folders add nothing to the address, and a folder like `[slug]` is a dynamic route: one entry, whose pages midcode finds in the running site.

| File | Page |
| --- | --- |
| `src/routes/+page.svelte` | `/` |
| `src/routes/(site)/about/+page.svelte` | `/about` |
| `src/routes/blog/[slug]/+page.svelte` | `/blog/[slug]` |

A Vite + Svelte app has no page list. Type a path in the page menu and press `Enter` to see another page. More in [Pages and navigation](https://midcode.app/docs/editor/pages.md).

### New pages (next release)

In the next release, "New page" in the page menu makes a SvelteKit page: a folder under `src/routes` with a `+page.svelte` in it.

```svelte title="src/routes/about/+page.svelte"
<main class="mx-auto max-w-3xl px-6 py-24">
  <h1 class="text-4xl font-semibold">About</h1>
  <p class="mt-4">A new page, made in midcode.</p>
</main>
```

The class names are written when Tailwind is in your `package.json`; without it the tags come bare. "Remove" moves the `+page.svelte` to the Trash, with its folder when it was the only file in it. A Vite + Svelte app keeps its pages in code, so neither is offered there.

## Site settings (next release)

In the next release the globe in the top bar opens Site settings. The title, the description, the language, search engines, the favicon and the social image are written as tags in the `<head>` of `src/app.html` in SvelteKit, and of `index.html` in Vite + Svelte:

```diff title="src/app.html"
 		%sveltekit.head%
+		<title>Oak Studio</title>
+		<meta name="description" content="Furniture made to last." />
 	</head>
```

A new tag goes last in the `<head>`, indented and closed like the tags around it. Images are copied into `static/` in SvelteKit and `public/` in Vite + Svelte. There's one form for the whole site: a title a page sets in its own `<svelte:head>` isn't read or written. Tried with SvelteKit. See [Site settings](https://midcode.app/docs/editor/site-settings.md).

## Styles

A `<style>` block keeps working: midcode doesn't read or change it. The style panel writes utility classes on the element.

With Tailwind 4 they're Tailwind's own: see [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md). Without it, midcode writes `mid:` utilities and compiles them to `midcode.css`, which it imports on your first style edit:

```diff title="src/routes/+layout.svelte"
 <script>
+  import './midcode.css'
   import favicon from '$lib/assets/favicon.svg'
```

If the project has no `src/routes/+layout.svelte`, midcode makes one that imports the stylesheet and renders the page (`{@render children()}`, or `<slot />` before Svelte 5). In Vite + Svelte the import goes in the module `index.html` loads, such as `src/main.ts`. See [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md).

## Limits

- [the free canvas](https://midcode.app/docs/editor/free-canvas.md) comes with the next release, for plain elements: a component or an element with an expression in it can't float. [variables](https://midcode.app/docs/editor/variables.md), new pages and Site settings aren't in the released version.
- A class that's computed is left alone: `class={…}`, a `class:name` directive, or a name glued to an expression (`btn-{size}`). midcode says the classes come from a variable and writes nothing.
- Text is edited in an element that holds only text. A paragraph with a link or a `<strong>` inside is edited in code. The link and the `<strong>` themselves can be edited.
- An element inside `{#each}` that uses the loop's values can't be moved out of its list, and nothing can be dropped inside a component instance.
- Elements move within one file.
- A component with no `<script>`, or one whose script declares no props and uses no runes, doesn't report where it's used: its elements are selected as plain elements of its file.
- Variants need a root midcode can write to. When the top of the file is a block (`{#if}`), when the root's `class` is an expression, or when props are taken as `let props = $props()` without a type, midcode says why it can't add one.
- Interactive components from Insert (Carousel, Tabs and the others) are React and can't go into a `.svelte` file: their tiles say "Needs a React page". Icons go in as inline SVG.
- midcode asks your compiler for Svelte 5's syntax tree. A project on Svelte 4 or older has not been tried.
- Styling without Tailwind has not been tried on a real SvelteKit project. Variants and states in `.svelte` components share their code with JSX components, but no Svelte project is on record as tried with them.

## Troubleshooting

**The site shows but nothing can be edited.** midcode fell back to your `dev` script. The dev server log (the button with the status dot in the top bar) says why.

**"Couldn't insert it: the code wouldn't parse".** The new markup wouldn't be valid where you dropped it. Nothing was written. Drop it somewhere else, or add it in code.

**"The text in the code doesn't match the preview."** The page hadn't reloaded after an earlier change. Wait for it and try again.

More in [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md).
