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
| Framework | Mount | Dispose | Client-only guaranteed by framework? |
|---|---|---|---|
Vue 3 / Nuxt 3 | onMounted | onUnmounted | Yes |
Svelte / SvelteKit | onMount | return value of onMount | Yes |
SolidStart | onMount | onCleanup | Yes |
Astro (default MPA) | inline <script> | — (page unload) | Yes |
Astro (View Transitions) | astro:page-load | astro:before-swap | Yes |
Important Notes
- There is no
frontalign-vue,frontalign-svelte,frontalign-solidor similar package — install onlyfrontalignand drive the coreFrontAlignclass yourself. - Import
frontalign/cssonce, ideally at the app root, regardless of framework. fa-toggle,fa-componentanddata-*attributes behave identically everywhere —FrontAlignfinds them by querying the rendered DOM, so the only framework-specific part is where you callnew FrontAlign()anddispose().- Construct
FrontAlignonly 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 aFrontAligninstance 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.