Other SSR Frameworks

FrontAlign core is plain, framework-agnostic JavaScript. There is no separate adapter package for Vue, Svelte, Solid or Astro — you install the same frontalign package and drive the FrontAlign class yourself from whichever lifecycle hook your framework gives you for "the component is now in the browser."

The FrontAlign constructor already guards for SSR internally (typeof window === "undefined" || typeof document === "undefined" short-circuits to a no-op), so it is always safe to import — the only rule that matters is: construct on mount, dispose on unmount.

Install

npm install frontalign

Import

import 'frontalign/css';
import { FrontAlign } from 'frontalign';

Universal Mount / Dispose Pattern

Regardless of framework, the contract is the same:

let fa;

// on mount (client-side only)
fa = new FrontAlign();

// on unmount / teardown
fa?.dispose();

dispose() disconnects the IntersectionObserver and MutationObserver, tears down every delegated behavior (navbar, dropdown, drawer, accordion, alert, form), and calls dispose() on every lazily-mounted component instance (Tooltip, Popover, Range, etc.) it created. Skipping it on route/component teardown leaks observers and event listeners across client-side navigations.

Vue 3 / Nuxt 3

Component-level

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { FrontAlign } from 'frontalign';

let fa;

onMounted(() => {
  fa = new FrontAlign();
});

onUnmounted(() => {
  fa?.dispose();
});
</script>

<template>
  <nav class="navbar">
    <a class="navbar-brand" href="/">FrontAlign</a>

    <button
      class="navbar-toggler"
      fa-toggle="navbar"
      data-target="#main-menu"
      aria-label="Toggle navigation"
    ></button>

    <div id="main-menu" class="navbar-menu"></div>
  </nav>
</template>

onMounted in Vue never runs during SSR — it only fires after hydration in the browser — so no extra import.meta.client guard is needed here.

App-wide (recommended)

For most apps it is simpler to mount FrontAlign once, globally, instead of per-component. Use a Nuxt client plugin (the .client.ts suffix restricts it to the browser build):

// plugins/frontalign.client.ts
import { FrontAlign } from 'frontalign';
import 'frontalign/css';

export default defineNuxtPlugin(() => {
  const fa = new FrontAlign();

  if (import.meta.hot) {
    import.meta.hot.dispose(() => fa.dispose());
  }
});

The import.meta.hot.dispose block only matters in dev — it prevents duplicate observers from stacking up across Vite HMR reloads of the plugin file.

Svelte / SvelteKit

<script>
  import { onMount } from 'svelte';
  import { FrontAlign } from 'frontalign';
  import 'frontalign/css';

  onMount(() => {
    const fa = new FrontAlign();
    return () => fa.dispose();
  });
</script>

<nav class="navbar">
  <a class="navbar-brand" href="/">FrontAlign</a>

  <button
    class="navbar-toggler"
    fa-toggle="navbar"
    data-target="#main-menu"
    aria-label="Toggle navigation"
  ></button>

  <div id="main-menu" class="navbar-menu"></div>
</nav>

Svelte's onMount never executes during SSR, and the function it returns is called automatically when the component is destroyed — this maps onto the mount/dispose contract with no extra plumbing. Put this in the root +layout.svelte for an app-wide instance instead of per-page if you don't need per-route teardown.

SolidStart

import { onMount, onCleanup } from 'solid-js';
import { FrontAlign } from 'frontalign';
import 'frontalign/css';

export default function RootLayout(props) {
  onMount(() => {
    const fa = new FrontAlign();
    onCleanup(() => fa.dispose());
  });

  return (
    <nav class="navbar">
      <a class="navbar-brand" href="/">FrontAlign</a>

      <button
        class="navbar-toggler"
        fa-toggle="navbar"
        data-target="#main-menu"
        aria-label="Toggle navigation"
      ></button>

      <div id="main-menu" class="navbar-menu"></div>
    </nav>
  );
}

Solid's JSX uses class, not className — do not port React examples over verbatim. Like the others, onMount only runs client-side; onCleanup is the matching teardown.

Astro

Astro is the odd one out: by default it ships static HTML with full page reloads between routes, so there is no persistent component instance to dispose of — the whole JS context is thrown away on navigation.

Default (MPA navigation)

---
// Navbar.astro
---
<nav class="navbar">
  <a class="navbar-brand" href="/">FrontAlign</a>

  <button
    class="navbar-toggler"
    fa-toggle="navbar"
    data-target="#main-menu"
    aria-label="Toggle navigation"
  ></button>

  <div id="main-menu" class="navbar-menu"></div>
</nav>

<script>
  import { FrontAlign } from 'frontalign';
  import 'frontalign/css';

  new FrontAlign();
</script>

No dispose() call is needed here — a full page navigation tears everything down for you.

With View Transitions (<ClientRouter />)

If the project opts into Astro's client-side routing, the DOM (and JS execution context) persists across "soft" navigations, so you're back to a real mount/dispose lifecycle, driven by Astro's navigation events:

<script>
  import { FrontAlign } from 'frontalign';
  import 'frontalign/css';

  let fa;

  function init() {
    fa = new FrontAlign();
  }

  document.addEventListener('astro:before-swap', () => fa?.dispose());
  document.addEventListener('astro:page-load', init);
</script>

astro:page-load fires on the initial load and after every swap, so it doubles as both the first mount and every remount; astro:before-swap is the dispose point right before the outgoing page is torn down.

Framework Lifecycle Reference

FrameworkMountDisposeClient-only guaranteed by framework?
Vue 3 / Nuxt 3onMountedonUnmountedYes
Svelte / SvelteKitonMountreturn value of onMountYes
SolidStartonMountonCleanupYes
Astro (default MPA)inline <script>— (page unload)Yes
Astro (View Transitions)astro:page-loadastro:before-swapYes

Important Notes

  • There is no frontalign-vue, frontalign-svelte, frontalign-solid or similar package — install only frontalign and drive the core FrontAlign class yourself.
  • Import frontalign/css once, ideally at the app root, regardless of framework.
  • fa-toggle, fa-component and data-* attributes behave identically everywhere — FrontAlign finds them by querying the rendered DOM, so the only framework-specific part is where you call new FrontAlign() and dispose().
  • Construct FrontAlign only once per real mount. Mounting it twice on the same page (duplicate layouts, HMR without cleanup, a missing dispose on route change) attaches duplicate observers and double-fires behaviors.
  • Static helpers — Modal.alert(), Modal.confirm(), Toast.show(), Alert.create(), DarkMode — are independent named exports. You can call them directly from any client-side event handler without holding a FrontAlign instance at all.
  • The constructor's internal SSR guard means an accidental new FrontAlign() on the server won't throw — it just does nothing — but that's a safety net, not a substitute for mounting it correctly on the client.

FrontAlign