Skip to content

Commit 5404201

Browse files
docs(solid-router): framer-motion example (#5818)
* docs(solid-router): framer-motion example * ci: apply automated fixes * add to examples --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 7d4df72 commit 5404201

File tree

12 files changed

+443
-2
lines changed

12 files changed

+443
-2
lines changed

docs/router/config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,10 @@
577577
"label": "View Transitions",
578578
"to": "framework/react/examples/view-transitions"
579579
},
580+
{
581+
"label": "Framer Motion",
582+
"to": "framework/react/examples/with-framer-motion"
583+
},
580584
{
581585
"label": "With tRPC",
582586
"to": "framework/react/examples/with-trpc"
@@ -633,6 +637,10 @@
633637
{
634638
"label": "View Transitions",
635639
"to": "framework/solid/examples/view-transitions"
640+
},
641+
{
642+
"label": "Framer Motion",
643+
"to": "framework/solid/examples/with-framer-motion"
636644
}
637645
]
638646
}
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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "tanstack-router-solid-example-with-framer-motion",
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+
"postcss": "^8.5.1",
16+
"redaxios": "^0.5.1",
17+
"solid-js": "^1.9.10",
18+
"solid-motionone": "^1.0.4",
19+
"tailwindcss": "^4.1.15",
20+
"zod": "^3.24.2"
21+
},
22+
"devDependencies": {
23+
"typescript": "^5.7.2",
24+
"vite": "^7.1.7",
25+
"vite-plugin-solid": "^2.11.10"
26+
}
27+
}
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: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { render } from 'solid-js/web'
2+
import { Motion, Presence } from 'solid-motionone'
3+
import {
4+
ErrorComponent,
5+
Link,
6+
Outlet,
7+
RouterProvider,
8+
createRootRoute,
9+
createRoute,
10+
createRouter,
11+
} from '@tanstack/solid-router'
12+
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
13+
import axios from 'redaxios'
14+
import './styles.css'
15+
16+
type PostType = {
17+
id: string
18+
title: string
19+
body: string
20+
}
21+
22+
const fetchPosts = async () => {
23+
console.info('Fetching posts...')
24+
await new Promise((r) => setTimeout(r, 500))
25+
return axios
26+
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
27+
.then((r) => r.data.slice(0, 10))
28+
}
29+
30+
const fetchPost = async (postId: string) => {
31+
console.info(`Fetching post with id ${postId}...`)
32+
await new Promise((r) => setTimeout(r, 500))
33+
const post = await axios
34+
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
35+
.then((r) => r.data)
36+
37+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
38+
if (!post) {
39+
throw new NotFoundError(`Post with id "${postId}" not found!`)
40+
}
41+
42+
return post
43+
}
44+
45+
export const mainTransitionProps = {
46+
initial: { y: -20, opacity: 0 },
47+
animate: { y: 0, opacity: 1 },
48+
exit: { y: 60, opacity: 0 },
49+
transition: {
50+
duration: 0.3,
51+
easing: 'ease-out',
52+
},
53+
} as const
54+
55+
export const postTransitionProps = {
56+
initial: { y: -20, opacity: 0 },
57+
animate: { y: 0, opacity: 1 },
58+
exit: { y: 60, opacity: 0 },
59+
transition: {
60+
duration: 0.3,
61+
easing: 'ease-out',
62+
},
63+
} as const
64+
65+
const rootRoute = createRootRoute({
66+
component: () => {
67+
return (
68+
<>
69+
<div class="p-2 flex gap-2 text-lg">
70+
<Link
71+
to="/"
72+
activeProps={{
73+
class: 'font-bold',
74+
}}
75+
activeOptions={{ exact: true }}
76+
>
77+
Home
78+
</Link>{' '}
79+
<Link
80+
to="/posts"
81+
activeProps={{
82+
class: 'font-bold',
83+
}}
84+
>
85+
Posts
86+
</Link>
87+
</div>
88+
<hr />
89+
<Outlet />
90+
{/* Start rendering router matches */}
91+
<TanStackRouterDevtools position="bottom-right" />
92+
</>
93+
)
94+
},
95+
})
96+
97+
const indexRoute = createRoute({
98+
getParentRoute: () => rootRoute,
99+
path: '/',
100+
component: () => {
101+
return (
102+
<Motion.div class="p-2" {...mainTransitionProps}>
103+
<h3>Welcome Home!</h3>
104+
</Motion.div>
105+
)
106+
},
107+
})
108+
109+
const postsLayoutRoute = createRoute({
110+
getParentRoute: () => rootRoute,
111+
path: 'posts',
112+
loader: () => fetchPosts(),
113+
component: () => {
114+
const posts = postsLayoutRoute.useLoaderData()
115+
return (
116+
<Motion.div class="p-2 flex gap-2" {...mainTransitionProps}>
117+
<ul class="list-disc pl-4">
118+
{[
119+
...posts(),
120+
{ id: 'i-do-not-exist', title: 'Non-existent Post' },
121+
].map((post) => {
122+
return (
123+
<li class="whitespace-nowrap">
124+
<Link
125+
to={postRoute.to}
126+
params={{
127+
postId: post.id,
128+
}}
129+
class="block py-1 text-blue-800 hover:text-blue-600"
130+
activeProps={{ class: 'text-black font-bold' }}
131+
>
132+
<div>{post.title.substring(0, 20)}</div>
133+
</Link>
134+
</li>
135+
)
136+
})}
137+
</ul>
138+
<hr />
139+
<Presence>
140+
<Outlet />
141+
</Presence>
142+
</Motion.div>
143+
)
144+
},
145+
})
146+
147+
const postsIndexRoute = createRoute({
148+
getParentRoute: () => postsLayoutRoute,
149+
path: '/',
150+
component: () => <div>Select a post.</div>,
151+
})
152+
153+
class NotFoundError extends Error {}
154+
155+
const postRoute = createRoute({
156+
getParentRoute: () => postsLayoutRoute,
157+
path: '$postId',
158+
loader: ({ params: { postId } }) => fetchPost(postId),
159+
errorComponent: ErrorComponent,
160+
component: () => {
161+
const post = postRoute.useLoaderData()
162+
return (
163+
<Motion.div class="space-y-2" {...postTransitionProps}>
164+
<h4 class="text-xl font-bold underline">{post().title}</h4>
165+
<div class="text-sm">{post().body}</div>
166+
</Motion.div>
167+
)
168+
},
169+
})
170+
171+
const routeTree = rootRoute.addChildren([
172+
postsLayoutRoute.addChildren([postRoute, postsIndexRoute]),
173+
indexRoute,
174+
])
175+
176+
// Set up a Router instance
177+
const router = createRouter({
178+
routeTree,
179+
defaultPreload: 'intent',
180+
scrollRestoration: true,
181+
context: {
182+
// loaderClient,
183+
},
184+
})
185+
186+
// Register things for typesafety
187+
declare module '@tanstack/solid-router' {
188+
interface Register {
189+
router: typeof router
190+
}
191+
}
192+
193+
const rootElement = document.getElementById('app')!
194+
195+
if (!rootElement.innerHTML) {
196+
render(() => <RouterProvider router={router} />, rootElement)
197+
}
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"esModuleInterop": true,
5+
"jsx": "preserve",
6+
"jsxImportSource": "solid-js",
7+
"skipLibCheck": true,
8+
"lib": ["DOM", "DOM.Iterable", "ES2022"]
9+
}
10+
}

0 commit comments

Comments
 (0)