npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

preact-iso

v2.6.2

Published

Isomorphic utilities for Preact

Downloads

11,359

Readme

preact-iso

Isomorphic async tools for Preact.

  • Lazy-load components using lazy() and <ErrorBoundary>, which also enables progressive hydration.
  • Generate static HTML for your app using prerender(), waiting for lazy() components and data dependencies.
  • Implement async-aware client and server-side routing using <Router>, including seamless async transitions.

lazy.js

Make a lazily-loaded version of a Component. lazy() takes an async function that resolves to a Component, and returns a wrapper version of that Component. The wrapper component can be rendered right away, even though the component is only loaded the first time it is rendered.

import { render } from 'preact';
import { ErrorBoundary, lazy, Router } from 'preact-iso';

// Synchronous, not code-splitted:
// import Home from './routes/home.js';
// import Profile from './routes/profile.js';

// Asynchronous, code-splitted:
const Home = lazy(() => import('./routes/home.js'));
const Profile = lazy(() => import('./routes/profile.js'));

const App = () => (
	<ErrorBoundary>
		<Router>
			<Home path="/" />
			<Profile path="/profile" />
		</Router>
	</ErrorBoundary>
);

render(<App />, document.body);

prerender.js

prerender() renders a Virtual DOM tree to an HTML string using preact-render-to-string. The difference is that it is asynchronous, and waits for any Promises thrown by components during rendering (Suspense-style) to resolve before returning the HTML. Nested promises also work, and the maximum depth can be controlled using the maxDepth option, which defaults to 10.

The Promise returned from prerender() resolves to an Object with html and links[] properties. The html property contains your pre-rendered static HTML markup, and links is an Array of any non-external URL strings found in links on the generated page.

import { ErrorBoundary, lazy, prerender } from 'preact-iso';

// Asynchronous (throws a promise)
const Foo = lazy(() => import('./foo.js'));

const App = () => (
	<ErrorBoundary>
		<Foo path="/" />
	</ErrorBoundary>
);

const { html, links } = await prerender(<App />, { maxDepth: 10 });

hydrate.js

hydrate() is a thin wrapper around Preact's hydrate() method. It performs hydration when the HTML for the current page includes pre-rendered output from prerender(). It falls back to plain rendering in any other cases, which is useful if you're not pre-rendering during development. This method also checks to make sure its running in a browser context before attempting any rendering - if not, it does nothing.

import { hydrate } from 'preact-iso';

const App = () => (
	<div class="app">
		<h1>Hello World</h1>
	</div>
);

hydrate(<App />);

router.js

A simple router for Preact with conventional and hooks-based APIs. The <Router> component is async-aware: when transitioning from one route to another, if the incoming route suspends (throws a Promise), the outgoing route is preserved until the new one becomes ready.

import { ErrorBoundary, lazy, LocationProvider, Router, useLocation } from 'preact-iso';

// Asynchronous (throws a promise)
const Home = lazy(() => import('./routes/home.js'));
const Profile = lazy(() => import('./routes/profile.js'));
const Profiles = lazy(() => import('./routes/profiles.js'));

const App = () => (
	<LocationProvider>
		<ErrorBoundary>
			<Router>
				<Home path="/" />
				<Profiles path="/profiles" />
				<Profile path="/profiles/:id" />
			</Router>
		</ErrorBoundary>
	</LocationProvider>
);

During prerendering, the generated HTML includes our full <Home> and <Profiles> component output because it waits for the lazy()-wrapped import() to resolve.

You can use the useRoute hook to get information of the route you are currently on.

Progressive Hydration: When the app is hydrated on the client, the route (Home or Profile in this case) suspends. This causes hydration for that part of the page to be deferred until the route's import() is resolved, at which point that part of the page automatically finishes hydrating.

Seamless Routing: Switch switching between routes on the client, the Router is aware of asynchronous dependencies in routes. Instead of clearing the current route and showing a loading spinner while waiting for the next route (or its data), the router preserves the current route in-place until the incoming route has finished loading, then they are swapped.

Nested Routing

Nested routes are supported by using multiple Router components. Partially matched routes end with a wildcard /* and the remaining value will be past to continue matching with if there are any further routes.

import { ErrorBoundary, LocationProvider, Router, Route } from 'preact-iso';

function ProfileA() {
	return <h2>A</h2>;
}

function ProfileB() {
	return <h2>B</h2>;
}

function Profile() {
	return (
		<div>
			<h1>Profile</h1>
			<ErrorBoundary>
				<Router>
					<Route path="/a" component={ProfileA} />
					<Route path="/b" component={ProfileB} />
				</Router>
			</ErrorBoundary>
		</div>
	);
}

const App = () => (
	<LocationProvider>
		<ErrorBoundary>
			<Router>
				<Route path="/" component={Home} />
				<Route path="/profiles/*" component={Profile} />
			</Router>
		</ErrorBoundary>
	</LocationProvider>
);