Skip to content

Instantly share code, notes, and snippets.

View Transitions

Scroll Restoration

When the user has a history stack with element transitions, it's possible for them to scroll the element out of view on one or both pages. This can cause elements to translate thousands of pixels across the document and should be avoided.

Here are a few product-level use cases that I would consider good practice with a the proverbial grid-to-hero-image CSS transform.

Scroll Position Priority

function App() {
return (
<Routes>
<Route path="/" element={<Root />}>
<Route index element={<Index />} />
<Route path="projects" element={<Projects />} />
</Route>
</Routes>
);
}
import type { V2_HtmlMetaDescriptor, V2_MetaFunction } from "@remix-run/node";
export const mergeMeta = (
overrideFn: V2_MetaFunction,
appendFn?: V2_MetaFunction,
): V2_MetaFunction => {
return arg => {
// get meta from parent routes
let mergedMeta = arg.matches.reduce((acc, match) => {
return acc.concat(match.meta || []);
import { riff, route, html, indexRoute } from "../riff/riff.mjs";
import { serve } from "../riff/riff-express.mjs";
import {
deleteTask,
getProjects,
getTasks,
getUser,
sendEmail,
updateTask,
createTask,
import { useFetcher } from "@remix-run/react";
import { useCallback, useMemo } from "react";
export function useRevalidator() {
let fetcher = useFetcher();
let revalidate = useCallback(
() => {
fetcher.submit(null, { action: "/", method: "post" });
},
type InitFunction = (send: SendFunction) => CleanupFunction;
type SendFunction = (event: string, data: string) => void;
type CleanupFunction = () => void;
export function eventStream(request: Request, init: InitFunction) {
let stream = new ReadableStream({
start(controller) {
let encoder = new TextEncoder();
let send = (event: string, data: string) => {
controller.enqueue(encoder.encode(`event: ${event}\n`));
import invariant from "tiny-invariant";
class AmalgoBox extends HTMLElement {
get input() {
return this.querySelector("input") as HTMLInputElement;
}
get button() {
return this.querySelector("button") as HTMLButtonElement;
}
// WIP, just finding all the boxes and glue, implementation is woefully incomplete
import type { DOMAttributes } from "react";
import { assign, createMachine, interpret } from "@xstate/fsm";
import invariant from "tiny-invariant";
type CustomElement<T> = Partial<
T & DOMAttributes<T> & { children: any; class: string }
>;
import { useLocation, useTransition } from "@remix-run/react";
import * as React from "react";
/**
* An enhanced `<details>` component that's intended to be used as a menu (a bit
* like a menu-button).
*/
export let DetailsMenu = React.forwardRef<
HTMLDetailsElement,
React.ComponentPropsWithRef<"details">
import localforage from "localforage";
import { matchSorter } from "match-sorter";
import sortBy from "sort-by";
export async function getContacts(query) {
await fakeNetwork(`getContacts:${query}`);
let contacts = await localforage.getItem("contacts");
if (!contacts) contacts = [];
if (query) {
contacts = matchSorter(contacts, query, { keys: ["first", "last"] });