Obra

Svelte and mdsvex for JetBrains IDEs

Svelte support for JetBrains IDEs, built in two layers — editing that always works with no Node and no server, and optional language-server intelligence on top. With first-class mdsvex: .svx files where markdown and Svelte are highlighted together, not one at the expense of the other.

Coming soon to the Marketplace Pricing to be announced


Svelte, as your IDE sees it — with nothing running

Every token below is coloured by a port of the same lexer that ships inside the plugin: the same seven states, the same keyword tables, the same rune sub-forms, the same fold regions. It is running here in your browser with no server, which is exactly the point — this is what a .svelte file looks like on a machine with no Node installed. Switch themes, collapse a block, read the edge cases.

<script lang="ts">
  // Runes are keywords, not function calls — the Svelte docs are explicit
  // about that, so they get a keyword colour rather than reading as calls.
  let { label = 'Clicks', onchange }: Props = $props();

  let count = $state(0);
  let doubled = $derived(count * 2);

  // Only DOCUMENTED sub-forms are absorbed into the rune…
  let history = $derived.by(() => {
    return Array.from({ length: count }, (_, i) => i + 1);
  });

  // …so a typo like this stays visibly a typo: $state, dot, identifier.
  let oops = $state.nope(0);

  $effect.pre(() => {
    document.title = `${label}: ${count}`;
  });
</script>

<button class="counter" onclick={() => count++}>
  {label}: {count} — doubled {doubled}
</button>

<style>
  /* Real CSS, from the IDE's own engine: selector, property and value
     each get their own colour, plus swatches and completion. */
  .counter {
    background: #ff5722;
    border-radius: 6px;
    padding: 0.5rem 1rem;
  }

  .counter:hover { filter: brightness(1.1); }
</style>
<!--
  Every {#…} block below folds, and so does this comment.
  The gutter arrows mark exactly what the editor collapses.
-->
{#await loadPosts()}
  <p class="loading">Loading…</p>
{:then posts}
  {#each posts as post, i (post.id)}
    {#if post.featured}
      <Featured {post} />
    {:else if i < 3}
      <article>
        <h2>{post.title}</h2>
        {@html post.excerpt}
      </article>
    {/if}
  {:else}
    <p class="empty">Nothing published yet.</p>
  {/each}
{:catch error}
  <p class="error">{error.message}</p>
{/await}

{#key page.id}
  <Chart data={page.stats} />
{/key}

{#snippet row(label, value)}
  <tr><th>{label}</th><td>{value}</td></tr>
{/snippet}

{@render row('Total', total)}

<!-- Not real syntax, and deliberately not coloured as if it were: -->
{:nope}
---
title: Shipping Svelte 5 runes
date: 2026-07-14
tags: [svelte, mdsvex]
---

<script>
  import Chart from '$lib/Chart.svelte';
  let views = $state(1_204);
</script>

# {title}

Published **{date}**, filed under _mdsvex_.

Prose and components share one document. This paragraph has `inline code`,
a [link](https://mdsvex.pngwn.io/), and a live count of {views} views —
markdown and Svelte, highlighted at the same time.

## What you are looking at

- Headings, **emphasis** and list markers are markdown
- `{expressions}` and <Chart data={views} /> are still Svelte
- Frontmatter above is YAML

```svelte
<!-- Inside a fence this is literal text, not markup. -->
{#if ready}<p>{value}</p>{/if}
```

> Indenting four spaces does nothing in mdsvex, so the plugin
> deliberately refuses to colour it as a code block.

---

<Chart data={views} height={240} />
<script>
  import { fade, fly } from 'svelte/transition';
  import { tooltip } from '$lib/actions';

  let name = $state('');
  let open = $state(false);
</script>

<svelte:window on:keydown={handleKey} />

<svelte:head>
  <title>{name || 'Untitled'}</title>
</svelte:head>

<form on:submit|preventDefault={save}>
  <input bind:value={name} use:tooltip={'Your name'} placeholder="Name">

  <div class:is-open={open} style:--gap="8px" transition:fly={{ y: 8 }}>
    <Panel bind:this={panel} let:item {...rest} />
  </div>

  <button in:fade out:fade animate:flip onclick={() => (open = !open)}>
    Toggle
  </button>

  <!-- `data-open` is a plain attribute: no directive prefix, no keyword colour. -->
  <span data-open={open}>{open ? 'open' : 'closed'}</span>
</form>

Try the arrows in the gutter. They mark exactly what the editor folds: <script> and <style> bodies, the five paired logic blocks — {#if}, {#each}, {#await}, {#key}, {#snippet} — and <!-- comments -->. Note what is not coloured as syntax: {:nope} is not a block marker and $state.nope is not a rune, because both sets are closed to exactly what the docs define.


Two layers, and the bottom one never fails

Most Svelte tooling for JetBrains is a language-server client and nothing else — no Node, no colours. This plugin is built the other way round: a complete offline editing layer first, with the server as an optional upgrade that adds types on top.

Always on

Works with no Node and no server

Everything here comes from the plugin's own lexer, running in-process. Open a .svelte file on a fresh machine, on a plane, in a Community edition — it is coloured, foldable and editable.

  • Syntax highlighting for markup, components, directives, {#if}-style logic blocks and {@render} template tags
  • Svelte 5 runes — $state, $derived, $effect, $props — coloured as the language keywords the Svelte docs define them to be, including sub-forms like $derived.by and $effect.pre
  • Real CSS inside <style> through the IDE's own CSS support: proper colouring of selectors, properties and values, plus colour swatches, completion and inspections
  • Code folding, matching-brace highlighting, and comment toggling that knows whether the caret sits in markup, script or style
  • Every colour customisable under Settings | Editor | Color Scheme | Svelte

When a server is running

Type-aware intelligence on top

Point the plugin at a svelte-language-server and the semantic layer overlays on the offline one. If it is missing, misconfigured, or your IDE has no LSP API, the layer above is still there — the file never goes grey.

  • Type-aware completion, hover, and signature help
  • Diagnostics from the Svelte and TypeScript compilers, surfaced inline as you type
  • Go to definition, find usages, and rename across components and props
  • The server ships with the plugin; a project-local svelte-language-server in node_modules is preferred when present
  • Paths configurable under Settings | Languages & Frameworks | Svelte

First-class mdsvex

.svx is its own language, not a compromise

Every other approach makes you pick: treat an .svx file as markdown and lose the components, or treat it as Svelte and lose the prose. This plugin lexes both at once. Headings, emphasis, links, lists, fenced code and YAML frontmatter are highlighted as markdown — while components and {expressions} stay live inside the prose.

Declared as a dialect of Svelte, so folding, brace matching, commenting and CSS injection all resolve through the base language. Only the lexer differs.


Works with your JetBrains IDE

The offline editing layer depends only on the platform and language modules, so it installs on every IDE above &mdash; Community editions included. The optional language-server layer needs an IDE with the LSP API; see the FAQ.


What you get

Offline syntax highlighting

Markup, components, attributes, directives, logic blocks and template tags — coloured by the plugin itself. No Node, no server, no network.

mdsvex <code>.svx</code> support

Markdown and Svelte highlighted together in one file, with frontmatter, fenced code, and live components in the prose.

Svelte 5 runes

$state, $derived, $effect, $props, $bindable, $inspect and $host read as keywords. Documented sub-forms are absorbed into the rune; $state.nope is left looking like the typo it is.

Real CSS in <code>&lt;style&gt;</code>

Style blocks are handed to the IDE's own CSS engine by language injection — selectors, properties and values each get their own colour, plus swatches, completion and inspections.

Folding that knows the blocks

Collapse <script> and <style> bodies, {#if}, {#each}, {#await}, {#key} and {#snippet} regions, and <!-- comments -->.

Context-aware commenting

/ emits <!-- --> in markup, // in script, and /* */ in style — because the commenter checks where the caret actually is.

Rune modules

.svelte.ts and .svelte.js files are sent to the language server while staying ordinary TypeScript and JavaScript files to the rest of the IDE.

Optional LSP layer

Add a language server for type-aware completion, inline diagnostics, go-to-definition, find usages and rename. Remove it and editing carries on unchanged.

Loads in Community editions

The LSP API only exists in the commercial IDEs, so it is an optional dependency. IntelliJ IDEA Community and PyCharm Community get the whole editing layer.

Open-ended compatibility

The IDE compatibility range has no upper bound, so upgrading your IDE does not mark the plugin incompatible and disable it.


Not on the Marketplace yet

Svelte for IntelliJ is still in development. It will follow the same model as our other plugins — a free trial, then a one-time purchase with a perpetual licence, no subscription. Final pricing will be announced when it ships.

Want to know when it lands? Email hello@obra.studio and we’ll tell you.


Help & FAQ

Do I need Node.js installed?

Not for editing. Highlighting, folding, brace matching, comment toggling and CSS injection all run inside the plugin, so a .svelte or .svx file opens fully coloured on a machine with no Node at all. Node 18+ is only needed if you want the optional language-server layer for types and diagnostics.

Which IDEs are supported?

The editing layer runs in any JetBrains IDE from 2024.3 onward, including the Community editions — it depends only on the platform and language modules. The language-server layer additionally needs an IDE with the LSP API: IntelliJ IDEA Ultimate, WebStorm, PhpStorm, PyCharm Professional, GoLand, Rider, RubyMine or RustRover. Where the API is unavailable the plugin still loads and edits normally.

What is mdsvex, and what exactly do I get?

mdsvex is markdown with Svelte components live inside it, usually written in .svx files. The plugin treats mdsvex as its own language rather than bolting it onto one or the other: markdown headings, emphasis, links, lists, fenced code and YAML frontmatter are highlighted as markdown, while components and {expressions} keep their Svelte colouring in the same paragraph. Markdown colours follow your existing Markdown colour scheme, so .svx files look like the .md files beside them.

Which file extensions does it recognise?

.svelte for Svelte components and .svx for mdsvex, out of the box. .svelte.ts and .svelte.js rune modules stay ordinary TypeScript and JavaScript files but are sent to the language server. You can associate further extensions under Settings → Editor → File Types.

How do I set up the language server?

You usually do not have to — a server ships with the plugin. If your project has its own svelte-language-server in node_modules, that one is preferred, so the plugin matches the version your build uses. Paths and the Node executable are configurable under Settings → Languages & Frameworks → Svelte.

Why are my <script> contents less colourful than my <style> contents?

That is deliberate. <style> bodies are injected into the IDE's real CSS support, which is safe because CSS is self-contained. <script> gets the plugin's own keyword, string, number and comment pass instead of a full JS injection — a component's script is not a valid standalone module, and injecting it produces a wall of false errors. Deep script intelligence is what the language-server layer is for.

Can I change the colours?

Yes. Every token type has its own key under Settings → Editor → Color Scheme → Svelte. Runes have a separate key from ordinary keywords, so you can make them stand out without touching the rest. Defaults fall back to your theme's platform attributes, so the plugin looks right under Darcula, IntelliJ Light and third-party schemes without shipping hard-coded colours.

Is there a free trial?

There will be — 14 days with every feature unlocked, run by the JetBrains Marketplace, no card and no account setup. The plugin is not on the Marketplace yet; email hello@obra.studio and we will tell you when it lands.

Will it be a subscription?

No. Like our other plugins it will be a one-time purchase with a perpetual licence — you keep using the version you bought for as long as you like, and bug fixes and compatibility updates for current JetBrains IDE versions are included.

Where can I report a bug or request a feature?

Email hello@obra.studio with as much detail as you can: IDE version, plugin version, and a minimal component or .svx file that reproduces the issue.