Skip to content

Commit f581aa9

Browse files
docs(solid-router): scroll restoration example (#5839)
* docs(solid-router): scroll restoration example * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 61ab402 commit f581aa9

File tree

12 files changed

+338
-0
lines changed

12 files changed

+338
-0
lines changed

docs/router/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,10 @@
650650
"label": "Authenticated Routes",
651651
"to": "framework/solid/examples/authenticated-routes"
652652
},
653+
{
654+
"label": "Scroll Restoration",
655+
"to": "framework/solid/examples/scroll-restoration"
656+
},
653657
{
654658
"label": "Deferred Data",
655659
"to": "framework/solid/examples/deferred-data"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.DS_Store
3+
dist
4+
dist-ssr
5+
*.local
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"files.watcherExclude": {
3+
"**/routeTree.gen.ts": true
4+
},
5+
"search.exclude": {
6+
"**/routeTree.gen.ts": true
7+
},
8+
"files.readonlyInclude": {
9+
"**/routeTree.gen.ts": true
10+
}
11+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Example
2+
3+
To run this example:
4+
5+
- `npm install` or `yarn`
6+
- `npm start` or `yarn start`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vite App</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "tanstack-router-solid-example-scroll-restoration",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "vite --port 3000",
7+
"build": "vite build && tsc --noEmit",
8+
"serve": "vite preview",
9+
"start": "vite"
10+
},
11+
"dependencies": {
12+
"@tailwindcss/postcss": "^4.1.15",
13+
"@tanstack/solid-router": "^1.135.2",
14+
"@tanstack/solid-router-devtools": "^1.135.2",
15+
"@tanstack/solid-virtual": "^3.13.0",
16+
"postcss": "^8.5.1",
17+
"solid-js": "^1.9.10",
18+
"tailwindcss": "^4.1.15"
19+
},
20+
"devDependencies": {
21+
"vite-plugin-solid": "^2.11.10",
22+
"typescript": "^5.7.2",
23+
"vite": "^7.1.7"
24+
}
25+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default {
2+
plugins: {
3+
'@tailwindcss/postcss': {},
4+
},
5+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { render } from 'solid-js/web'
2+
import {
3+
Link,
4+
Outlet,
5+
RouterProvider,
6+
createRootRoute,
7+
createRoute,
8+
createRouter,
9+
useElementScrollRestoration,
10+
} from '@tanstack/solid-router'
11+
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
12+
import { createVirtualizer } from '@tanstack/solid-virtual'
13+
import './styles.css'
14+
15+
const rootRoute = createRootRoute({
16+
component: RootComponent,
17+
})
18+
19+
function RootComponent() {
20+
return (
21+
<>
22+
<div class="p-2 flex gap-2 sticky top-0 border-b bg-gray-100 dark:bg-gray-900">
23+
<Link to="/" class="[&.active]:font-bold">
24+
Home
25+
</Link>{' '}
26+
<Link to="/about" class="[&.active]:font-bold">
27+
About
28+
</Link>
29+
<Link to="/about" resetScroll={false}>
30+
About (No Reset)
31+
</Link>
32+
<Link to="/by-element" class="[&.active]:font-bold">
33+
By-Element
34+
</Link>
35+
</div>
36+
<Outlet />
37+
<TanStackRouterDevtools />
38+
</>
39+
)
40+
}
41+
42+
const indexRoute = createRoute({
43+
getParentRoute: () => rootRoute,
44+
path: '/',
45+
loader: () => new Promise<void>((r) => setTimeout(r, 500)),
46+
component: IndexComponent,
47+
})
48+
49+
function IndexComponent() {
50+
return (
51+
<div class="p-2">
52+
<h3>Welcome Home!</h3>
53+
<div class="space-y-2">
54+
{Array.from({ length: 50 }).map((_, i) => (
55+
<div class="h-[100px] p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border">
56+
Home Item {i + 1}
57+
</div>
58+
))}
59+
</div>
60+
</div>
61+
)
62+
}
63+
64+
const aboutRoute = createRoute({
65+
getParentRoute: () => rootRoute,
66+
path: '/about',
67+
loader: () => new Promise<void>((r) => setTimeout(r, 500)),
68+
component: AboutComponent,
69+
})
70+
71+
function AboutComponent() {
72+
return (
73+
<div class="p-2">
74+
<div>Hello from About!</div>
75+
<div class="space-y-2">
76+
{Array.from({ length: 50 }).map((_, i) => (
77+
<div class="h-[100px] p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border">
78+
About Item {i + 1}
79+
</div>
80+
))}
81+
</div>
82+
</div>
83+
)
84+
}
85+
86+
const byElementRoute = createRoute({
87+
getParentRoute: () => rootRoute,
88+
path: '/by-element',
89+
loader: () => new Promise<void>((r) => setTimeout(r, 500)),
90+
component: ByElementComponent,
91+
})
92+
93+
function ByElementComponent() {
94+
// We need a unique ID for manual scroll restoration on a specific element
95+
// It should be as unique as possible for this element across your app
96+
const scrollRestorationId = 'myVirtualizedContent'
97+
98+
// We use that ID to get the scroll entry for this element
99+
const scrollEntry = useElementScrollRestoration({
100+
id: scrollRestorationId,
101+
})
102+
103+
// Let's use TanStack Virtual to virtualize some content!
104+
let virtualizerParentRef: any
105+
const virtualizer = createVirtualizer({
106+
count: 10000,
107+
getScrollElement: () => virtualizerParentRef,
108+
estimateSize: () => 100,
109+
// We pass the scrollY from the scroll restoration entry to the virtualizer
110+
// as the initial offset
111+
initialOffset: scrollEntry?.scrollY,
112+
})
113+
114+
return (
115+
<div class="p-2 h-[calc(100vh-41px)] flex flex-col">
116+
<div>Hello from By-Element!</div>
117+
<div class="h-full min-h-0 flex gap-4">
118+
<div class="border rounded-lg p-2 overflow-auto flex-1 space-y-2">
119+
{Array.from({ length: 50 }).map((_, i) => (
120+
<div class="h-[100px] p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border">
121+
About Item {i + 1}
122+
</div>
123+
))}
124+
</div>
125+
<div class="flex-1 overflow-auto flex flex-col gap-4">
126+
{Array.from({ length: 2 }).map((_, i) => (
127+
<div class="flex-1 border rounded-lg p-2 overflow-auto">
128+
<div class="space-y-2">
129+
{Array.from({ length: 50 }).map((_, i) => (
130+
<div class="h-[100px] p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border">
131+
About Item {i + 1}
132+
</div>
133+
))}
134+
</div>
135+
</div>
136+
))}
137+
<div class="flex-1 flex flex-col min-h-0">
138+
<div class="font-bold">Virtualized</div>
139+
<div
140+
ref={virtualizerParentRef}
141+
// We pass the scroll restoration ID to the element
142+
// as a custom attribute that will get picked up by the
143+
// scroll restoration watcher
144+
data-scroll-restoration-id={scrollRestorationId}
145+
class="flex-1 border rounded-lg overflow-auto relative"
146+
>
147+
<div
148+
style={{
149+
height: `${virtualizer.getTotalSize()}px`,
150+
}}
151+
>
152+
{virtualizer.getVirtualItems().map((item) => (
153+
<div
154+
class="absolute p-2 pb-0 w-full"
155+
style={{
156+
height: item.size + 'px',
157+
top: item.start + 'px',
158+
}}
159+
>
160+
<div class="p-2 rounded-lg bg-gray-200 dark:bg-gray-800 border h-full">
161+
Virtualized Item {item.index + 1}
162+
</div>
163+
</div>
164+
))}
165+
</div>
166+
</div>
167+
</div>
168+
</div>
169+
</div>
170+
</div>
171+
)
172+
}
173+
174+
const routeTree = rootRoute.addChildren([
175+
indexRoute,
176+
aboutRoute,
177+
byElementRoute,
178+
])
179+
180+
const router = createRouter({
181+
routeTree,
182+
defaultPreload: 'intent',
183+
scrollRestoration: true,
184+
})
185+
186+
declare module '@tanstack/solid-router' {
187+
interface Register {
188+
router: typeof router
189+
}
190+
}
191+
192+
const rootElement = document.getElementById('app')!
193+
194+
if (!rootElement.innerHTML) {
195+
render(() => <RouterProvider router={router} />, rootElement)
196+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@import 'tailwindcss';
2+
3+
@layer base {
4+
*,
5+
::after,
6+
::before,
7+
::backdrop,
8+
::file-selector-button {
9+
border-color: var(--color-gray-200, currentcolor);
10+
}
11+
}
12+
13+
html {
14+
color-scheme: light dark;
15+
}
16+
* {
17+
@apply border-gray-200 dark:border-gray-800;
18+
}
19+
body {
20+
@apply bg-gray-50 text-gray-950 dark:bg-gray-900 dark:text-gray-200;
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"esModuleInterop": true,
5+
"jsx": "preserve",
6+
"jsxImportSource": "solid-js",
7+
"target": "ESNext",
8+
"moduleResolution": "Bundler",
9+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
10+
"skipLibCheck": true
11+
}
12+
}

0 commit comments

Comments
 (0)