Skip to content

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.

View as Markdown

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, 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 withvite dev, with a config of midcode’s that loads yours and puts one plugin first
Elements are marked byThat plugin, before Svelte compiles each .svelte file, using your project’s svelte/compiler
EditingText, static classes, attributes, tags; insert, move, duplicate, remove
ComponentsInstances, props, variants and states. Variables in the next release
PagesSvelteKit: every +page under src/routes, and new ones in the next release. Vite + Svelte: none listed
StylesTailwind 4 classes, or mid: classes with midcode.css imported in src/routes/+layout.svelte
Tried withA 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.

Terminal
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.

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.

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>
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 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:

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.

  • 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 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.

  • 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.

CMS, Languages, Code view and Publish work as in any project.

Several elements at onceNext 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.

FilePage
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.

New pagesNext 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.

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 settingsNext 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:

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.

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. Without it, midcode writes mid: utilities and compiles them to midcode.css, which it imports on your first style edit:

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.

Limits

  • the free canvas comes with the next release, for plain elements: a component or an element with an expression in it can’t float. variables, 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.