Skip to main content Svelte Highlight v7.21.1

TSRX language preview

1. Statement container

TypeScript setup, then one JSX output, plus a scoped style block
horizon-dark
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}
atom-one-dark
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}
github-dark
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}
dracula
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}
nord
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}
github
export function Greeting({ name }: { name?: string }) @{
  const message = name ? `Hello, ${name}` : "Hello, stranger";

  <>
    <div class="card">
      <p>{message}</p>
    </div>

    <style>
      .card { padding: 1rem; }
    </style>
  </>
}

2. Template control flow

@for, @if/@else, and @empty as first-class template directives
horizon-dark
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}
atom-one-dark
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}
github-dark
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}
dracula
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}
nord
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}
github
function UserList({ users, showBio }: Props) @{
  @for (const user of users) {
    const role = user.admin ? "Admin" : "Member";

    <div class="user-row">
      <strong>{user.name}</strong>
      <span>{role}</span>
      @if (showBio && user.bio) {
        <p>{user.bio}</p>
      } @else {
        <p>No bio</p>
      }
    </div>
  } @empty {
    <p>No users</p>
  }
}

3. Lazy prop destructure

&{ ... } compiles to lazy getters for reactive reads
horizon-dark
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}
atom-one-dark
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}
github-dark
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}
dracula
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}
nord
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}
github
function Counter(&{ count, label }: Props) @{
  <section>
    <h2>{label}</h2>
    <p>Count: {count}</p>
  </section>
}

Kitchen sink

horizon-dark
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}
atom-one-dark
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}
github-dark
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}
dracula
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}
nord
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}
github
type User = {
  id: number;
  name: string;
  admin?: boolean;
  bio?: string;
};

interface Props {
  users: User[];
  title?: string;
}

// Lazy prop reads stay reactive.
export function Dashboard(&{ users, title }: Props) @{
  const heading = title ?? "Team";

  <>
    {/* One JSX tree is the function output; no return. */}
    <header class="banner">
      <h1>{heading}</h1>
      <Badge count={users.length} />
    </header>

    @for (const user of users) {
      // Setup before the row is still TypeScript.
      const role = user.admin ? "Admin" : "Member";

      <article class="user-row">
        <strong>{user.name}</strong>
        <span>{role}</span>
        @if (user.bio) {
          <p>{user.bio}</p>
        } @else {
          <p class="muted">No bio</p>
        }
      </article>
    } @empty {
      <p>No users yet</p>
    }

    @switch (users.length) {
      @case (0) {
        <p>Invite someone</p>
      }
      @default {
        <p>{users.length} on the roster</p>
      }
    }

    /* Stats can suspend; this is the fallback. */
    @try {
      <Stats users={users} />
    } @catch (error) {
      <p class="error">{error.message}</p>
    } @pending {
      <p>Loading stats</p>
    }

    <style>
      /* Scoped to this component. */
      .banner {
        padding: 1rem;
      }

      .user-row {
        display: flex;
        gap: 0.5rem;
      }

      .muted {
        color: #888;
      }

      .error {
        color: crimson;
      }
    </style>
  </>
}

function Badge({ count }: { count: number }) @{
  <span class="badge">{count}</span>
}

// Tuple form of the same lazy destructure.
function Pair(&[first, second]: [string, string]) @{
  <p>{first} / {second}</p>
}