partial-html
v0.1.1
Published
Small declarative partial HTML update polyfill for template-for patching.
Maintainers
Readme
partial-html
Small browser polyfill for declarative partial HTML updates.
It supports the early WICG patching shape:
<div>
<!--?start name="profile"-->
Loading...
<!--?end-->
</div>
<template for="profile">
<strong>Ada Lovelace</strong>
</template>After partial-html runs, the placeholder is replaced by the template content.
The package also installs a small subset of the proposed unsafe HTML setter APIs, including appendHTMLUnsafe() and buffered streamAppendHTMLUnsafe().
partial-html also includes Google's template-for-polyfill package and loads it from browser-only code paths. This keeps JSX/SSR imports safe while still using the official <template for> polyfill in the browser.
Install
Core JavaScript / plain HTML:
npm install partial-htmlReact:
npm install partial-html reactReact frameworks with SSR:
npm install partial-html react react-domImport the entry point that matches your target:
// Core JavaScript / browser
import { installPartialHTML } from "partial-html";
// or
import { installPartialHTML } from "partial-html/core";
// Official template-for-polyfill loader
import { loadTemplateForPolyfill } from "partial-html/template-for-polyfill";
// React / JSX / SSR-safe client components
import { PartialHTMLProvider, PartialOutlet } from "partial-html/jsx";Core JavaScript / Plain HTML
<!doctype html>
<html lang="en">
<body>
<main>
<h1>Profile</h1>
<section id="profile">
<!--?start name="profile"-->
Loading profile...
<!--?end-->
</section>
</main>
<script type="module">
import { installPartialHTML, partialTemplate } from "https://esm.sh/partial-html";
installPartialHTML();
async function loadProfile() {
const html = partialTemplate(
"profile",
`<article>
<h2>Ada Lovelace</h2>
<p>First computer programmer.</p>
</article>`,
);
document.body.appendHTMLUnsafe(html);
}
loadProfile();
</script>
</body>
</html>JSX / React
import {
PartialHTMLProvider,
PartialOutlet,
usePartialPatch,
} from "partial-html/jsx";
export function Profile() {
return (
<PartialHTMLProvider>
<ProfileContent />
</PartialHTMLProvider>
);
}
function ProfileContent() {
const { patch } = usePartialPatch();
async function loadProfile() {
patch(
"profile",
`<article>
<h2>Ada Lovelace</h2>
<p>First computer programmer.</p>
</article>`,
);
}
return (
<>
<button type="button" onClick={loadProfile}>
Load profile
</button>
<PartialOutlet
name="profile"
fallback={
<div aria-busy="true">
<strong>Loading profile...</strong>
<p>Please wait while profile details load.</p>
</div>
}
as="section"
/>
</>
);
}More complete examples are available in:
examples/core-js/index.htmlexamples/jsx/ProfilePartial.tsx
SSR
partial-html/jsx is safe to import in SSR builds. It does not touch window or document during server render. The browser polyfill is installed from useEffect, so DOM patching starts after hydration.
For Next.js App Router, put the patching component in a client component:
"use client";
import {
PartialHTMLProvider,
PartialOutlet,
usePartialPatch,
} from "partial-html/jsx";
export function ProfilePartial() {
return (
<PartialHTMLProvider>
<ProfileContent />
</PartialHTMLProvider>
);
}
function ProfileContent() {
const { patch } = usePartialPatch();
return (
<>
<button
type="button"
onClick={() => patch("profile", "<strong>Ada Lovelace</strong>")}
>
Load profile
</button>
<PartialOutlet
name="profile"
fallback={
<div aria-busy="true">
<strong>Loading profile...</strong>
</div>
}
as="section"
/>
</>
);
}SSR behavior:
- Server render outputs the JSX fallback markup.
- Hydration keeps that markup stable.
PartialOutletinserts the marker comments around the fallback in the browser.PartialHTMLProviderinstalls the polyfill in the browser after mount.usePartialPatch()patches the DOM only in the browser.- Server-side template patching is not performed, because the proposal and polyfill are DOM-based.
API
installPartialHTML(options?: {
root?: ParentNode;
observe?: boolean;
installHtmlSetters?: boolean;
templateForPolyfill?: boolean;
}): () => void;
loadTemplateForPolyfill(): Promise<boolean>;
processPartialHTML(root?: ParentNode): { patched: number };
partialPlaceholder(name: string, fallback?: string): string;
partialMarker(name: string): string;
partialTemplate(name: string, html: string): string;
insertPartialHTML(target: Element, html: string, position?: "set" | "replace" | "before" | "after" | "append" | "prepend"): { patched: number };
installHTMLSetters(): void;JSX entry:
PartialHTMLProvider(props: {
children?: React.ReactNode;
observe?: boolean;
installHtmlSetters?: boolean;
templateForPolyfill?: boolean;
});
PartialOutlet(props: {
name: string;
fallback?: React.ReactNode;
as?: keyof React.JSX.IntrinsicElements;
});
usePartialPatch(): {
patch(name: string, html: string): void;
};Notes
- Browsers currently parse
<?start name="x">and similar processing instructions as comments intext/html, so this package intentionally supports comment markers like<!--?start name="x"-->. - The official
template-for-polyfillpackage is loaded by default insideinstallPartialHTML()whendocumentexists. Pass{ templateForPolyfill: false }to use only this package's lightweight patcher. streamHTMLUnsafe()and related stream methods are buffered and applied when the stream closes.- This package does not sanitize HTML. Use it only with trusted HTML or sanitize before passing strings into unsafe setters.
Development
npm run build
npm run test
npm run test:bundlenpm run test runs named Node test cases. npm run test:bundle runs the webpack/Babel transpilation fixture against the published entry points.
Publish
From the monorepo root:
npm run publish:partial-htmlThat command increments the patch version before publishing. Use these for larger version changes:
npm run publish:partial-html:minor
npm run publish:partial-html:majorIf npm requires 2FA, run the version bump and publish command separately:
npm version patch --workspace partial-html --no-git-tag-version
npm publish --workspace partial-html --otp YOUR_CODE