CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-svelte

Svelte is a compiler-based UI framework that transforms declarative component code into efficient imperative JavaScript that surgically updates the DOM

Overview
Eval results
Files

legacy.mddocs/reference/

Svelte Legacy Compatibility API

The svelte/legacy module provides backward compatibility for Svelte 4 code running in Svelte 5. This module contains deprecated functions and types that bridge the gap between Svelte 4's class-based components and Svelte 5's function-based components.

Important: All exports from this module are deprecated and should only be used as temporary solutions during migration from Svelte 4 to Svelte 5. See the Svelte 5 migration guide for recommended modern patterns.


Component Compatibility Functions

createClassComponent()

function createClassComponent<
  Props extends Record<string, any>,
  Exports extends Record<string, any>,
  Events extends Record<string, any>,
  Slots extends Record<string, any>
>(
  options: ComponentConstructorOptions<Props> & {
    component: ComponentType<SvelteComponent<Props, Events, Slots>> | Component<Props>;
  }
): SvelteComponent<Props, Events, Slots> & Exports

{ .api }

Creates a Svelte 4 compatible component instance from a component function, taking the same options as a Svelte 4 component constructor.

Parameters:

  • options - Component constructor options including:
    • target - The DOM element to mount the component to
    • anchor - Optional element to render before
    • props - Component properties
    • component - The component function to instantiate
    • context - Context map
    • hydrate - Whether to hydrate existing DOM
    • intro - Whether to play intro transitions

Returns: A Svelte 4 style component instance with $set, $on, and $destroy methods

Status: Deprecated - Use this only as a temporary solution to migrate your imperative component code to Svelte 5.

Example:

import { createClassComponent } from 'svelte/legacy';
import MyComponent from './MyComponent.svelte';

// Svelte 4 style instantiation
const instance = createClassComponent({
  target: document.getElementById('app'),
  component: MyComponent,
  props: {
    name: 'World'
  }
});

// Use Svelte 4 style methods
instance.$set({ name: 'Universe' });
instance.$on('click', (event) => {
  console.log('Component clicked', event.detail);
});

// Clean up when done
instance.$destroy();

Migration Guide:

In Svelte 5, use the mount() function instead:

// Svelte 5 way
import { mount } from 'svelte';
import MyComponent from './MyComponent.svelte';

const instance = mount(MyComponent, {
  target: document.getElementById('app'),
  props: {
    name: 'World'
  }
});

// Update props using the component's exported functions
// or use callback props instead of events

asClassComponent()

function asClassComponent<
  Props extends Record<string, any>,
  Exports extends Record<string, any>,
  Events extends Record<string, any>,
  Slots extends Record<string, any>
>(
  component: SvelteComponent<Props, Events, Slots> | Component<Props>
): ComponentType<SvelteComponent<Props, Events, Slots> & Exports>

{ .api }

Wraps a Svelte 5 component function as a Svelte 4 compatible class component constructor.

Parameters:

  • component - A Svelte 5 component function

Returns: A constructor that can be instantiated with new like Svelte 4 components

Status: Deprecated - Use this only as a temporary solution to migrate your imperative component code to Svelte 5.

Example:

import { asClassComponent } from 'svelte/legacy';
import MyComponent from './MyComponent.svelte';

// Create a Svelte 4-style constructor
const ComponentClass = asClassComponent(MyComponent);

// Now can be used like Svelte 4 components
const instance = new ComponentClass({
  target: document.getElementById('app'),
  props: { count: 0 }
});

// Supports Svelte 4 methods
instance.$set({ count: 5 });
instance.$on('change', handler);
instance.$destroy();

Use Case:

This is particularly useful when:

  • Integrating Svelte 5 components into legacy code that expects class components
  • Gradually migrating a large codebase
  • Working with libraries that expect Svelte 4-style constructors

Migration Guide:

Refactor code to use mount() directly instead of relying on class instantiation:

// Before (Svelte 4)
const instance = new MyComponent({
  target: element,
  props: { value: 10 }
});

// After (Svelte 5)
import { mount } from 'svelte';
const instance = mount(MyComponent, {
  target: element,
  props: { value: 10 }
});

Reactive Behavior Function

run()

function run(fn: () => void | (() => void)): void

{ .api }

Runs the given function once immediately on the server, and works like $effect.pre on the client. Provides Svelte 4-style reactive behavior.

Parameters:

  • fn - Function to execute. Can optionally return a cleanup function.

Status: Deprecated - Use this only as a temporary solution to migrate your component code to Svelte 5.

Example:

import { run } from 'svelte/legacy';

// Inside a component
run(() => {
  console.log('This runs immediately on server, as $effect.pre on client');

  // Cleanup function (optional)
  return () => {
    console.log('Cleanup when effect re-runs or component unmounts');
  };
});

Migration Guide:

Replace run() with appropriate Svelte 5 runes:

// Instead of run()
import { run } from 'svelte/legacy';

run(() => {
  // Side effect code
  return () => {
    // Cleanup
  };
});

// Use $effect.pre in Svelte 5
$effect.pre(() => {
  // Side effect code
  return () => {
    // Cleanup
  };
});

For server-only code, use conditional logic:

import { onMount } from 'svelte';

// Server-side (runs during SSR)
if (typeof window === 'undefined') {
  // Server code
}

// Client-side only
onMount(() => {
  // Client code
});

Event Handler Utilities

Svelte 4 used event modifiers like on:click|preventDefault which are not available in Svelte 5. These utilities provide functional alternatives.

handlers()

function handlers(...handlers: EventListener[]): EventListener

{ .api }

Combines multiple event listeners into a single listener function. Mimics the ability to have multiple listeners in Svelte 4.

Parameters:

  • ...handlers - Variable number of event listener functions

Returns: A single event listener that calls all handlers in sequence

Status: Deprecated

Example:

<script>
  import { handlers } from 'svelte/legacy';

  function logEvent(e) {
    console.log('Event:', e.type);
  }

  function trackEvent(e) {
    analytics.track(e.type);
  }

  const handleClick = handlers(logEvent, trackEvent);
</script>

<button onclick={handleClick}>Click me</button>

Migration Guide:

In Svelte 5, simply define a function that calls multiple handlers:

<script>
  function handleClick(e) {
    logEvent(e);
    trackEvent(e);
  }
</script>

<button onclick={handleClick}>Click me</button>

trusted()

function trusted(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the trusted event modifier. Only executes the handler if event.isTrusted is true (i.e., the event was triggered by user action, not programmatically).

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that checks event.isTrusted

Status: Deprecated

Example:

<script>
  import { trusted } from 'svelte/legacy';

  function handleSubmit(e) {
    // Only processes user-initiated submissions
    console.log('Form submitted by user');
  }

  const onSubmit = trusted(handleSubmit);
</script>

<form onsubmit={onSubmit}>
  <!-- ... -->
</form>

Migration Guide:

<!-- Svelte 4 -->
<form on:submit|trusted={handleSubmit}>

<!-- Svelte 5 -->
<script>
  function handleSubmit(e) {
    if (!e.isTrusted) return;
    // Handle trusted event
  }
</script>

<form onsubmit={handleSubmit}>

self()

function self(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the self event modifier. Only executes the handler if event.target === event.currentTarget (i.e., the event originated on this element, not a child).

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that checks if event target is the current target

Status: Deprecated

Example:

<script>
  import { self } from 'svelte/legacy';

  function handleDivClick(e) {
    console.log('Div itself clicked, not child elements');
  }

  const onClick = self(handleDivClick);
</script>

<div onclick={onClick}>
  Click the div background (not the button)
  <button>Button</button>
</div>

Migration Guide:

<!-- Svelte 4 -->
<div on:click|self={handleClick}>

<!-- Svelte 5 -->
<script>
  function handleClick(e) {
    if (e.target !== e.currentTarget) return;
    // Handle click on element itself
  }
</script>

<div onclick={handleClick}>

stopPropagation()

function stopPropagation(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the stopPropagation event modifier. Calls event.stopPropagation() before executing the handler.

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that stops event propagation

Status: Deprecated

Example:

<script>
  import { stopPropagation } from 'svelte/legacy';

  function handleButtonClick(e) {
    console.log('Button clicked, event will not bubble to parent');
  }

  const onClick = stopPropagation(handleButtonClick);
</script>

<div onclick={() => console.log('This will not fire')}>
  <button onclick={onClick}>Click me</button>
</div>

Migration Guide:

<!-- Svelte 4 -->
<button on:click|stopPropagation={handleClick}>

<!-- Svelte 5 -->
<script>
  function handleClick(e) {
    e.stopPropagation();
    // Handle click
  }
</script>

<button onclick={handleClick}>

once()

function once(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the once event modifier. Ensures the handler only executes once, then removes itself.

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that only fires once

Status: Deprecated

Example:

<script>
  import { once } from 'svelte/legacy';

  function handleFirstClick(e) {
    console.log('This will only log once');
  }

  const onClick = once(handleFirstClick);
</script>

<button onclick={onClick}>Click me multiple times</button>

Migration Guide:

<!-- Svelte 4 -->
<button on:click|once={handleClick}>

<!-- Svelte 5 -->
<script>
  let clicked = $state(false);

  function handleClick(e) {
    if (clicked) return;
    clicked = true;
    // Handle first click
  }
</script>

<button onclick={handleClick}>

<!-- Or use addEventListener with { once: true } -->
<script>
  import { onMount } from 'svelte';

  let button;

  onMount(() => {
    button.addEventListener('click', handleClick, { once: true });
  });
</script>

<button bind:this={button}>

stopImmediatePropagation()

function stopImmediatePropagation(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the stopImmediatePropagation event modifier. Calls event.stopImmediatePropagation() before executing the handler, preventing other listeners on the same element from firing.

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that stops immediate propagation

Status: Deprecated

Example:

<script>
  import { stopImmediatePropagation } from 'svelte/legacy';

  function firstHandler(e) {
    console.log('First handler - prevents other handlers');
  }

  function secondHandler(e) {
    console.log('This will never fire');
  }

  const onClick1 = stopImmediatePropagation(firstHandler);
</script>

<button onclick={onClick1} onclick={secondHandler}>
  Click me
</button>

Migration Guide:

<!-- Svelte 4 -->
<button on:click|stopImmediatePropagation={handleClick}>

<!-- Svelte 5 -->
<script>
  function handleClick(e) {
    e.stopImmediatePropagation();
    // Handle click
  }
</script>

<button onclick={handleClick}>

preventDefault()

function preventDefault(
  fn: (event: Event, ...args: Array<unknown>) => void
): (event: Event, ...args: unknown[]) => void

{ .api }

Substitute for the preventDefault event modifier. Calls event.preventDefault() before executing the handler.

Parameters:

  • fn - Event handler function

Returns: Wrapped handler that prevents default behavior

Status: Deprecated

Example:

<script>
  import { preventDefault } from 'svelte/legacy';

  function handleSubmit(e) {
    console.log('Form submitted without page reload');
  }

  const onSubmit = preventDefault(handleSubmit);
</script>

<form onsubmit={onSubmit}>
  <input type="text" />
  <button type="submit">Submit</button>
</form>

Migration Guide:

<!-- Svelte 4 -->
<form on:submit|preventDefault={handleSubmit}>

<!-- Svelte 5 -->
<script>
  function handleSubmit(e) {
    e.preventDefault();
    // Handle submit
  }
</script>

<form onsubmit={handleSubmit}>

Combining Event Modifiers

Multiple modifiers can be combined:

<script>
  import { preventDefault, stopPropagation, once } from 'svelte/legacy';

  function handleClick(e) {
    console.log('Handled with all modifiers');
  }

  // Combine multiple modifiers
  const onClick = once(stopPropagation(preventDefault(handleClick)));
</script>

<button onclick={onClick}>Click me</button>

<!-- Migration to Svelte 5 -->
<script>
  let clicked = $state(false);

  function handleClick(e) {
    if (clicked) return;
    clicked = true;

    e.preventDefault();
    e.stopPropagation();

    // Handle click
  }
</script>

<button onclick={handleClick}>Click me</button>

Event Modifier Actions

passive()

function passive(
  node: HTMLElement,
  [event, handler]: [event: string, handler: () => EventListener]
): void

{ .api }

Substitute for the passive event modifier, implemented as an action. Adds an event listener with { passive: true } option, improving scrolling performance for touch/wheel events.

Parameters:

  • node - The HTML element to attach the listener to
  • [event, handler] - Tuple of event name and handler function

Status: Deprecated

Example:

<script>
  import { passive } from 'svelte/legacy';

  function handleScroll(e) {
    // This listener won't block scrolling
    console.log('Scrolled');
  }
</script>

<div use:passive={['scroll', handleScroll]}>
  Scrollable content
</div>

Migration Guide:

<!-- Svelte 5 -->
<script>
  import { onMount } from 'svelte';

  let element;

  onMount(() => {
    element.addEventListener('scroll', handleScroll, { passive: true });

    return () => {
      element.removeEventListener('scroll', handleScroll);
    };
  });

  function handleScroll(e) {
    console.log('Scrolled');
  }
</script>

<div bind:this={element}>
  Scrollable content
</div>

<!-- Or create a reusable action -->
<script>
  function passiveAction(node, { event, handler }) {
    node.addEventListener(event, handler, { passive: true });

    return {
      destroy() {
        node.removeEventListener(event, handler);
      }
    };
  }

  function handleScroll(e) {
    console.log('Scrolled');
  }
</script>

<div use:passiveAction={{ event: 'scroll', handler: handleScroll }}>
  Scrollable content
</div>

nonpassive()

function nonpassive(
  node: HTMLElement,
  [event, handler]: [event: string, handler: () => EventListener]
): void

{ .api }

Substitute for the nonpassive event modifier, implemented as an action. Adds an event listener with { passive: false } option, allowing preventDefault() in touch/wheel events.

Parameters:

  • node - The HTML element to attach the listener to
  • [event, handler] - Tuple of event name and handler function

Status: Deprecated

Example:

<script>
  import { nonpassive } from 'svelte/legacy';

  function handleTouchMove(e) {
    // Can call preventDefault() to prevent scrolling
    e.preventDefault();
    console.log('Touch moved, scrolling prevented');
  }
</script>

<div use:nonpassive={['touchmove', handleTouchMove]}>
  Touch-interactive content
</div>

Migration Guide:

<!-- Svelte 5 -->
<script>
  import { onMount } from 'svelte';

  let element;

  onMount(() => {
    element.addEventListener('touchmove', handleTouchMove, { passive: false });

    return () => {
      element.removeEventListener('touchmove', handleTouchMove);
    };
  });

  function handleTouchMove(e) {
    e.preventDefault();
    console.log('Touch moved');
  }
</script>

<div bind:this={element}>
  Touch-interactive content
</div>

Event Bubbling Utility

createBubbler()

function createBubbler(): (type: string) => (event: Event) => boolean

{ .api }

Creates an event bubbling function that mimics the behavior of on:click without a handler in Svelte 4. This allows events to bubble up the component tree.

Returns: A function that takes an event type and returns an event handler

Status: Deprecated - Use this only as a temporary solution to migrate your automatically delegated events in Svelte 5.

Example:

<!-- Parent.svelte -->
<script>
  import { createBubbler } from 'svelte/legacy';
  import Child from './Child.svelte';

  function handleClick(e) {
    console.log('Event bubbled from child:', e.detail);
  }
</script>

<Child on:customEvent={handleClick} />

<!-- Child.svelte -->
<script>
  import { createBubbler } from 'svelte/legacy';

  const bubble = createBubbler();

  function handleInternalClick() {
    // Bubble event to parent
    const event = new CustomEvent('customEvent', {
      detail: { data: 'hello' },
      bubbles: true
    });
    bubble('customEvent')(event);
  }
</script>

<button onclick={handleInternalClick}>Click to bubble</button>

Migration Guide:

In Svelte 5, use callback props instead of event bubbling:

<!-- Parent.svelte (Svelte 5) -->
<script>
  import Child from './Child.svelte';

  function handleCustomEvent(detail) {
    console.log('Event from child:', detail);
  }
</script>

<Child onCustomEvent={handleCustomEvent} />

<!-- Child.svelte (Svelte 5) -->
<script>
  let { onCustomEvent } = $props();

  function handleInternalClick() {
    onCustomEvent?.({ data: 'hello' });
  }
</script>

<button onclick={handleInternalClick}>Click to notify parent</button>

Or use event dispatching on the host element with $host():

<!-- Child.svelte (Svelte 5 with custom element) -->
<script>
  function handleInternalClick() {
    const host = $host();
    host.dispatchEvent(new CustomEvent('customEvent', {
      detail: { data: 'hello' },
      bubbles: true
    }));
  }
</script>

<button onclick={handleInternalClick}>Click</button>

Types

LegacyComponentType

type LegacyComponentType = {
  new (o: ComponentConstructorOptions): SvelteComponent;
  (...args: Parameters<Component<Record<string, any>>>): ReturnType<Component<Record<string, any>, Record<string, any>>>;
}

{ .api }

A type that supports using a component as both a class (Svelte 4 style) and a function (Svelte 5 style) during the transition period.

Example:

import type { LegacyComponentType } from 'svelte/legacy';
import MyComponent from './MyComponent.svelte';

// Component can be used both ways
const ComponentAsClass: LegacyComponentType = MyComponent;

// Instantiate as class
const instance1 = new ComponentAsClass({
  target: document.body,
  props: { value: 10 }
});

// Or use as function (Svelte 5 style)
// Note: actual function usage happens through mount()

SvelteComponent (from main module)

class SvelteComponent<
  Props extends Record<string, any> = Record<string, any>,
  Events extends Record<string, any> = any,
  Slots extends Record<string, any> = any
>

{ .api }

Base class for Svelte 4 components. In Svelte 5, components are functions, not classes. This class is provided for backward compatibility and type checking.

Constructor:

constructor(options: ComponentConstructorOptions<Props>)

Deprecated: This constructor only exists when using the asClassComponent compatibility helper. Migrate toward using mount instead.

Properties:

  • static element?: typeof HTMLElement - The custom element version (only present if compiled with customElement option)
  • $$prop_def: Props - For type checking only, does not exist at runtime
  • $$events_def: Events - For type checking only, does not exist at runtime
  • $$slot_def: Slots - For type checking only, does not exist at runtime
  • $$bindings?: string - For type checking only, does not exist at runtime

Methods:

$destroy()

$destroy(): void

Destroys the component, cleaning up listeners and DOM elements.

Deprecated: This method only exists when using legacy compatibility helpers.

Example:

const instance = new MyComponent({ target: document.body });
// Later...
instance.$destroy();

$on()

$on<K extends Extract<keyof Events, string>>(
  type: K,
  callback: (e: Events[K]) => void
): () => void

Attaches an event listener. Returns an unsubscribe function.

Deprecated: This method only exists when using legacy compatibility helpers.

Example:

const instance = new MyComponent({ target: document.body });

const unsubscribe = instance.$on('change', (event) => {
  console.log('Changed:', event.detail);
});

// Later...
unsubscribe();

$set()

$set(props: Partial<Props>): void

Updates component props programmatically.

Deprecated: This method only exists when using legacy compatibility helpers.

Example:

const instance = new MyComponent({
  target: document.body,
  props: { count: 0 }
});

// Update props
instance.$set({ count: 5 });

Migration to Svelte 5:

// Svelte 4
const instance = new MyComponent({
  target: document.body,
  props: { count: 0 }
});
instance.$set({ count: 5 });
instance.$on('change', handler);
instance.$destroy();

// Svelte 5
import { mount, unmount } from 'svelte';

const instance = mount(MyComponent, {
  target: document.body,
  props: { count: 0 }
});

// Props are reactive - update through component's API
// Use callback props instead of $on
// Use unmount instead of $destroy
unmount(instance);

ComponentConstructorOptions

interface ComponentConstructorOptions<
  Props extends Record<string, any> = Record<string, any>
>

{ .api }

Options for instantiating Svelte 4-style class components.

Properties:

  • target: Element | Document | ShadowRoot - Required. The DOM element to render the component into
  • anchor?: Element - Optional element to render immediately before
  • props?: Props - Initial component properties
  • context?: Map<any, any> - Context values accessible via getContext()
  • hydrate?: boolean - If true, hydrate existing DOM instead of creating new elements
  • intro?: boolean - If true, play intro transitions on initial render
  • recover?: boolean - If true, attempt to recover from errors during hydration
  • sync?: boolean - If true, flush updates synchronously
  • idPrefix?: string - Prefix for element IDs (for SSR)
  • $$inline?: boolean - Internal flag for inline components

Status: Deprecated - In Svelte 4, components are classes. In Svelte 5, they are functions. Use mount instead to instantiate components.

Example:

import type { ComponentConstructorOptions } from 'svelte';
import MyComponent from './MyComponent.svelte';

const options: ComponentConstructorOptions<{ count: number }> = {
  target: document.getElementById('app'),
  props: {
    count: 0
  },
  intro: true,
  context: new Map([['theme', 'dark']])
};

const instance = new MyComponent(options);

Migration to Svelte 5:

Use MountOptions with the mount() function:

import { mount } from 'svelte';
import type { MountOptions } from 'svelte';
import MyComponent from './MyComponent.svelte';

const options: MountOptions<{ count: number }> = {
  target: document.getElementById('app'),
  props: {
    count: 0
  },
  intro: true,
  context: new Map([['theme', 'dark']])
};

const instance = mount(MyComponent, options);

Complete Migration Example

Here's a comprehensive example showing migration from Svelte 4 to Svelte 5 patterns:

Svelte 4 Component

<!-- Counter.svelte (Svelte 4) -->
<script>
  import { createEventDispatcher } from 'svelte';

  export let count = 0;
  export let max = 10;

  const dispatch = createEventDispatcher();

  function increment() {
    if (count < max) {
      count += 1;
      dispatch('change', { count });
    }
  }

  function reset() {
    count = 0;
    dispatch('reset');
  }

  export function getValue() {
    return count;
  }
</script>

<div>
  <p>Count: {count}</p>
  <button on:click|preventDefault|stopPropagation={increment}>
    Increment
  </button>
  <button on:click|once={reset}>Reset</button>
</div>

Svelte 4 Usage

// app.js (Svelte 4)
import Counter from './Counter.svelte';

const counter = new Counter({
  target: document.getElementById('app'),
  props: {
    count: 5,
    max: 20
  }
});

counter.$on('change', (event) => {
  console.log('Count changed:', event.detail.count);
});

counter.$on('reset', () => {
  console.log('Counter reset');
});

// Update props
counter.$set({ max: 15 });

// Get exported value
const value = counter.getValue();

// Clean up
counter.$destroy();

Svelte 5 Component

<!-- Counter.svelte (Svelte 5) -->
<script>
  let {
    count = $bindable(0),
    max = 10,
    onChange,
    onReset
  } = $props();

  function increment(e) {
    e.preventDefault();
    e.stopPropagation();

    if (count < max) {
      count += 1;
      onChange?.({ count });
    }
  }

  let resetClicked = $state(false);

  function reset(e) {
    if (resetClicked) return;
    resetClicked = true;

    count = 0;
    onReset?.();
  }

  export function getValue() {
    return count;
  }
</script>

<div>
  <p>Count: {count}</p>
  <button onclick={increment}>Increment</button>
  <button onclick={reset}>Reset</button>
</div>

Svelte 5 Usage

// app.js (Svelte 5)
import { mount } from 'svelte';
import Counter from './Counter.svelte';

const counter = mount(Counter, {
  target: document.getElementById('app'),
  props: {
    count: 5,
    max: 20,
    onChange: (detail) => {
      console.log('Count changed:', detail.count);
    },
    onReset: () => {
      console.log('Counter reset');
    }
  }
});

// Props are reactive through bindable
// Update via component's exported API or state

// Get exported value
const value = counter.getValue();

// Clean up
import { unmount } from 'svelte';
unmount(counter);

Transition Helper (Using Legacy Module)

// transition-helper.js
// Temporary bridge during migration
import { asClassComponent } from 'svelte/legacy';
import Counter from './Counter.svelte';

// Export a Svelte 4-compatible version
export const CounterCompat = asClassComponent(Counter);

// Can be used in legacy code
const instance = new CounterCompat({
  target: document.getElementById('app'),
  props: { count: 0 }
});

instance.$on('change', (e) => console.log(e.detail));
instance.$destroy();

Best Practices for Migration

1. Use Legacy Module Sparingly

The legacy module should only be used as a temporary bridge. Create a migration plan to update all code to Svelte 5 patterns.

2. Prioritize Migration of Event Modifiers

Event modifiers are straightforward to migrate:

// Create a utility module
// event-utils.js
export function withPreventDefault(handler) {
  return (e) => {
    e.preventDefault();
    handler(e);
  };
}

export function withStopPropagation(handler) {
  return (e) => {
    e.stopPropagation();
    handler(e);
  };
}

// Use in components
import { withPreventDefault } from './event-utils';

<button onclick={withPreventDefault(handleClick)}>

3. Replace Component Events with Callback Props

<!-- Before (Svelte 4) -->
<script>
  import { createEventDispatcher } from 'svelte';
  const dispatch = createEventDispatcher();

  function notify() {
    dispatch('notification', { message: 'Hello' });
  }
</script>

<!-- After (Svelte 5) -->
<script>
  let { onNotification } = $props();

  function notify() {
    onNotification?.({ message: 'Hello' });
  }
</script>

4. Update Component Instantiation

// Before (Svelte 4)
import Component from './Component.svelte';
const instance = new Component({
  target: element,
  props: { value: 10 }
});

// After (Svelte 5)
import { mount } from 'svelte';
import Component from './Component.svelte';
const instance = mount(Component, {
  target: element,
  props: { value: 10 }
});

5. Replace $set with Bindable Props

<!-- Component.svelte (Svelte 5) -->
<script>
  let { value = $bindable(0) } = $props();
</script>

<!-- Parent can bind and update -->
<script>
  let count = $state(0);

  function updateCount() {
    count = 10; // Automatically updates child
  }
</script>

<Component bind:value={count} />
<button onclick={updateCount}>Update</button>

6. Testing During Migration

// test-utils.js
import { asClassComponent } from 'svelte/legacy';

export function makeLegacyComponent(Component) {
  if (process.env.LEGACY_MODE) {
    return asClassComponent(Component);
  }
  return Component;
}

// Use in tests
import { makeLegacyComponent } from './test-utils';
import Counter from './Counter.svelte';

const Component = makeLegacyComponent(Counter);
const instance = new Component({
  target: container,
  props: { count: 0 }
});

Common Migration Patterns

Pattern 1: Event Modifier Composition

// legacy-events.js
import {
  preventDefault,
  stopPropagation,
  once,
  trusted
} from 'svelte/legacy';

export function composeModifiers(handler, modifiers) {
  let composed = handler;

  if (modifiers.includes('preventDefault')) {
    composed = preventDefault(composed);
  }
  if (modifiers.includes('stopPropagation')) {
    composed = stopPropagation(composed);
  }
  if (modifiers.includes('once')) {
    composed = once(composed);
  }
  if (modifiers.includes('trusted')) {
    composed = trusted(composed);
  }

  return composed;
}

// Usage
const handler = composeModifiers(handleClick, [
  'preventDefault',
  'stopPropagation'
]);

<button onclick={handler}>Click</button>

Pattern 2: Legacy Component Wrapper

// LegacyWrapper.js
import { createClassComponent } from 'svelte/legacy';

export class LegacyAdapter {
  constructor(Component) {
    this.Component = Component;
  }

  mount(options) {
    return createClassComponent({
      ...options,
      component: this.Component
    });
  }
}

// Usage
import { LegacyAdapter } from './LegacyWrapper';
import MyComponent from './MyComponent.svelte';

const adapter = new LegacyAdapter(MyComponent);
const instance = adapter.mount({
  target: document.body,
  props: { value: 10 }
});

Pattern 3: Progressive Migration

<!-- HybridComponent.svelte -->
<script>
  // Support both Svelte 4 and 5 patterns
  import { createEventDispatcher } from 'svelte';

  // Svelte 4 style
  export let value = 0;
  const dispatch = createEventDispatcher();

  // Svelte 5 style props (optional)
  let { onChange } = $props() || {};

  function handleChange(newValue) {
    value = newValue;

    // Support both patterns
    dispatch('change', { value });
    onChange?.({ value });
  }
</script>

Troubleshooting

Issue: Component not working with legacy code

Solution: Use asClassComponent to create a compatible wrapper:

import { asClassComponent } from 'svelte/legacy';
import MyComponent from './MyComponent.svelte';

const CompatComponent = asClassComponent(MyComponent);
export default CompatComponent;

Issue: Events not bubbling

Solution: Use createBubbler or migrate to callback props:

import { createBubbler } from 'svelte/legacy';

const bubble = createBubbler();

// Bubble custom events
function handleInternalEvent(e) {
  bubble('customEvent')(e);
}

Issue: Event modifiers not working

Solution: Use the legacy event modifier functions or migrate to manual checks:

import { preventDefault, stopPropagation } from 'svelte/legacy';

// Temporary solution
const handler = preventDefault(stopPropagation(handleClick));

// Better: Migrate to Svelte 5 pattern
function handleClick(e) {
  e.preventDefault();
  e.stopPropagation();
  // Handle event
}

See Also

  • Svelte 5 Migration Guide
  • Components are no longer classes
  • Event changes
  • Main module documentation
  • Events and actions documentation
  • Lifecycle and components documentation

Install with Tessl CLI

npx tessl i tessl/npm-svelte

docs

index.md

tile.json