A plugin for Deno Fresh that turns your site into a single page application.
Checkout this page for a live demo.
The plugin adds mtos to your website.
You still serve the static HTML files, but the user experience is the same as SPA with incremental requests via fetch API on the client side. And you can also add transition animations, progress bar, etc.
-
Create a fresh project if you haven't done so.
-
Add
fresh_mtos
to yourimport_map.json
{ "imports": { "$fresh/": "https://deno.land/x/fresh@1.1.1/", + "$fresh_mtos/": "https://deno.land/x/fresh_mtos/", "$std/": "https://deno.land/std@0.145.0/", "$gfm": "https://deno.land/x/gfm@0.1.22/mod.ts", "preact": "https://esm.sh/preact@10.11.0", "preact/": "https://esm.sh/preact@10.11.0/", "preact-render-to-string": "https://esm.sh/*preact-render-to-string@5.2.4", "@preact/signals": "https://esm.sh/*@preact/signals@1.0.3", "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.0.1", "twind": "https://esm.sh/twind@0.16.17", "twind/": "https://esm.sh/twind@0.16.17/" } }
-
Create a configuration file called
mtos.config.ts
.import type { Options } from "$fresh_mtos/mod.ts"; export default { selfURL: import.meta.url, } as Options;
You can also use the
defineConfig
API.import { defineConfig } from "$fresh_mtos/mod.ts"; export default defineConfig({ selfURL: import.meta.url, });
-
Add the plugin to your
main.ts
/// <reference no-default-lib="true" /> /// <reference lib="dom" /> /// <reference lib="dom.iterable" /> /// <reference lib="dom.asynciterable" /> /// <reference lib="deno.ns" /> import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; import twindPlugin from "$fresh/plugins/twind.ts"; import twindConfig from "./twind.config.ts"; import mtosConfig from "./mtos.config.ts"; import mtosPlugin from "$fresh_mtos/mod.ts"; await start(manifest, { plugins: [ twindPlugin(twindConfig), mtosPlugin(mtosConfig), ], });
The below options can be used to setup mtos.
interface Options {
/** Eval Script Element When Update, Default is false */
eval?: boolean;
/** Fetch Options */
fetch?: RequestInit;
/** Auto Scroll Behavior */
scroll?: {
enable?: boolean;
left?: number;
top?: number;
behavior?: "auto" | "smooth";
};
/** Fetch Hooks */
onMatch: (a: HTMLAnchorElement) => boolean;
onFetchStart?(href: string): boolean | undefined | void;
onFetchEnd?: (html: string, href: string) => string | undefined | void;
onFetchError?: (error: Error, href: string) => void;
/** Render Hooks */
onBeforePageRendered?: (href: string) => void;
onPageRendered?: (href: string) => void;
/** Dom Patch Hooks */
getNodeKey?: (node: Node) => any;
onBeforeNodeAdded?: (node: Node) => Node;
onNodeAdded?: (node: Node) => Node;
onBeforeElUpdated?: (fromEl: HTMLElement, toEl: HTMLElement) => boolean;
onElUpdated?: (el: HTMLElement) => void;
onBeforeNodeDiscarded?: (node: Node) => boolean;
onNodeDiscarded?: (node: Node) => void;
onBeforeElChildrenUpdated?: (
fromEl: HTMLElement,
toEl: HTMLElement,
) => boolean;
}
Copyright (c) 2022, Raven Satir