├── .gitignore
├── .npmrc
├── README.md
├── apps
├── 0-json-server
│ ├── .gitignore
│ ├── db.json
│ └── package.json
├── 1-spa
│ ├── 1-1-vite-componentdidmount
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── AppProviders.tsx
│ │ │ ├── AppRoutes.tsx
│ │ │ ├── api-types.ts
│ │ │ ├── main.tsx
│ │ │ ├── pages
│ │ │ │ ├── HomePage.tsx
│ │ │ │ └── PostPage.tsx
│ │ │ ├── theme.ts
│ │ │ ├── vite-env.d.ts
│ │ │ └── withRouter.tsx
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 1-2-vite-useeffect
│ │ ├── .eslintrc.cjs
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── AppProviders.tsx
│ │ │ ├── AppRoutes.tsx
│ │ │ ├── api-types.ts
│ │ │ ├── main.tsx
│ │ │ ├── pages
│ │ │ │ ├── HomePage.tsx
│ │ │ │ ├── PostPage.tsx
│ │ │ │ ├── UserPage.tsx
│ │ │ │ └── UsersPage.tsx
│ │ │ ├── theme.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 1-3-vite-reactquery
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── AppProviders.tsx
│ │ │ ├── AppRoutes.tsx
│ │ │ ├── api-types.ts
│ │ │ ├── main.tsx
│ │ │ ├── pages
│ │ │ │ ├── HomePage.tsx
│ │ │ │ ├── PostPage.tsx
│ │ │ │ ├── UserPage.tsx
│ │ │ │ └── UsersPage.tsx
│ │ │ ├── theme.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── 1-4-vite-reactquery-refactor
│ │ ├── .gitignore
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ └── vite.svg
│ │ ├── src
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── AppProviders.tsx
│ │ │ ├── AppRoutes.tsx
│ │ │ ├── api-types.ts
│ │ │ ├── hooks
│ │ │ │ ├── useGetPost.ts
│ │ │ │ ├── useGetPostComments.ts
│ │ │ │ ├── useGetPosts.ts
│ │ │ │ ├── useGetUser.ts
│ │ │ │ ├── useGetUserPosts.ts
│ │ │ │ └── useGetUsers.ts
│ │ │ ├── main.tsx
│ │ │ ├── pages
│ │ │ │ ├── HomePage.tsx
│ │ │ │ ├── PostPage.tsx
│ │ │ │ ├── UserPage.tsx
│ │ │ │ └── UsersPage.tsx
│ │ │ ├── theme.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ └── 1-5-astro-react-spa
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ ├── extensions.json
│ │ └── launch.json
│ │ ├── README.md
│ │ ├── astro.config.mjs
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ └── favicon.svg
│ │ ├── src
│ │ ├── assets
│ │ │ ├── astro.svg
│ │ │ └── background.svg
│ │ ├── layouts
│ │ │ └── Layout.astro
│ │ ├── pages
│ │ │ ├── index.astro
│ │ │ └── spa
│ │ │ │ └── [...slug].astro
│ │ ├── spa
│ │ │ ├── App.tsx
│ │ │ ├── AppLayout.tsx
│ │ │ ├── AppProviders.tsx
│ │ │ ├── AppRoutes.tsx
│ │ │ ├── api-types.ts
│ │ │ ├── pages
│ │ │ │ ├── HomePage.tsx
│ │ │ │ ├── PostPage.tsx
│ │ │ │ ├── UserPage.tsx
│ │ │ │ └── UsersPage.tsx
│ │ │ └── theme.ts
│ │ └── styles
│ │ │ └── global.css
│ │ └── tsconfig.json
├── 2-ssg
│ ├── 2-1-nextjs-ssg
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── next.config.mjs
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── next.svg
│ │ │ └── vercel.svg
│ │ ├── src
│ │ │ ├── api-types.ts
│ │ │ ├── components
│ │ │ │ └── AppLayout.tsx
│ │ │ ├── pages
│ │ │ │ ├── _app.tsx
│ │ │ │ ├── _document.tsx
│ │ │ │ ├── api
│ │ │ │ │ └── hello.ts
│ │ │ │ ├── index.tsx
│ │ │ │ └── posts
│ │ │ │ │ └── [id].tsx
│ │ │ └── theme.ts
│ │ └── tsconfig.json
│ ├── 2-2-astro-ssg
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ │ ├── extensions.json
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── astro.config.mjs
│ │ ├── package.json
│ │ ├── public
│ │ │ └── favicon.svg
│ │ ├── src
│ │ │ ├── api-types.ts
│ │ │ ├── assets
│ │ │ │ ├── astro.svg
│ │ │ │ └── background.svg
│ │ │ ├── layouts
│ │ │ │ └── Layout.astro
│ │ │ ├── pages
│ │ │ │ ├── index.astro
│ │ │ │ └── posts
│ │ │ │ │ └── [id].astro
│ │ │ └── styles
│ │ │ │ └── global.css
│ │ └── tsconfig.json
│ └── 2-3-astro-react-ssg
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ ├── extensions.json
│ │ └── launch.json
│ │ ├── README.md
│ │ ├── astro.config.mjs
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ └── favicon.svg
│ │ ├── src
│ │ ├── api-types.ts
│ │ ├── assets
│ │ │ ├── astro.svg
│ │ │ └── background.svg
│ │ ├── components
│ │ │ ├── AppLayout.tsx
│ │ │ ├── HomePage.tsx
│ │ │ ├── PostPage.tsx
│ │ │ └── ReactProviders.tsx
│ │ ├── layouts
│ │ │ └── Layout.astro
│ │ ├── pages
│ │ │ ├── index.astro
│ │ │ └── posts
│ │ │ │ └── [id].astro
│ │ ├── styles
│ │ │ └── global.css
│ │ └── theme.ts
│ │ └── tsconfig.json
├── 3-ssr
│ ├── 3-1-nextjs-ssr
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── next.config.mjs
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── next.svg
│ │ │ └── vercel.svg
│ │ ├── src
│ │ │ ├── api-types.ts
│ │ │ ├── components
│ │ │ │ └── AppLayout.tsx
│ │ │ ├── pages
│ │ │ │ ├── _app.tsx
│ │ │ │ ├── _document.tsx
│ │ │ │ ├── api
│ │ │ │ │ └── hello.ts
│ │ │ │ ├── index.tsx
│ │ │ │ └── posts
│ │ │ │ │ └── [id].tsx
│ │ │ └── theme.ts
│ │ └── tsconfig.json
│ ├── 3-2-remix-ssr
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── app
│ │ │ ├── api-types.ts
│ │ │ ├── components
│ │ │ │ └── AppLayout.tsx
│ │ │ ├── entry.client.tsx
│ │ │ ├── entry.server.tsx
│ │ │ ├── root.tsx
│ │ │ ├── routes
│ │ │ │ ├── _index.tsx
│ │ │ │ └── posts.$id.tsx
│ │ │ └── theme.ts
│ │ ├── package.json
│ │ ├── postcss.config.cjs
│ │ ├── public
│ │ │ └── favicon.ico
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── 3-3-sveltekit-ssr
│ │ ├── .gitignore
│ │ ├── .npmrc
│ │ ├── .prettierignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── postcss.config.js
│ │ ├── src
│ │ │ ├── app.css
│ │ │ ├── app.d.ts
│ │ │ ├── app.html
│ │ │ ├── lib
│ │ │ │ ├── api-types.ts
│ │ │ │ ├── components
│ │ │ │ │ └── AppLayout.svelte
│ │ │ │ └── index.ts
│ │ │ └── routes
│ │ │ │ ├── +layout.svelte
│ │ │ │ ├── +page.server.ts
│ │ │ │ ├── +page.svelte
│ │ │ │ └── posts
│ │ │ │ └── [id]
│ │ │ │ ├── +page.server.ts
│ │ │ │ └── +page.svelte
│ │ ├── static
│ │ │ └── favicon.png
│ │ ├── svelte.config.js
│ │ ├── tailwind.config.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ └── 3-4-astro-ssr
│ │ ├── .gitignore
│ │ ├── .vscode
│ │ ├── extensions.json
│ │ └── launch.json
│ │ ├── README.md
│ │ ├── astro.config.mjs
│ │ ├── package.json
│ │ ├── public
│ │ └── favicon.svg
│ │ ├── src
│ │ ├── api-types.ts
│ │ ├── assets
│ │ │ ├── astro.svg
│ │ │ └── background.svg
│ │ ├── layouts
│ │ │ └── Layout.astro
│ │ ├── pages
│ │ │ ├── api
│ │ │ │ ├── add-comment.ts
│ │ │ │ └── delete-comment.ts
│ │ │ ├── index.astro
│ │ │ └── posts
│ │ │ │ └── [id].astro
│ │ └── styles
│ │ │ └── global.css
│ │ └── tsconfig.json
└── 4-streaming
│ ├── 4-1-nextjs-rsc
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── src
│ │ ├── api-types.ts
│ │ ├── app
│ │ │ ├── favicon.ico
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ └── posts
│ │ │ │ └── [id]
│ │ │ │ ├── CommentSection.tsx
│ │ │ │ ├── actions.tsx
│ │ │ │ ├── error.tsx
│ │ │ │ ├── loading.tsx
│ │ │ │ └── page.tsx
│ │ ├── components
│ │ │ └── AppLayout.tsx
│ │ └── theme.ts
│ └── tsconfig.json
│ ├── 4-2-nextjs-rsc-react-query
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── src
│ │ ├── api-types.ts
│ │ ├── app
│ │ │ ├── PostsFeed.tsx
│ │ │ ├── ReactQueryProvider.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ └── posts
│ │ │ │ └── [id]
│ │ │ │ ├── CommentSection.tsx
│ │ │ │ ├── actions.tsx
│ │ │ │ ├── error.tsx
│ │ │ │ ├── loading.tsx
│ │ │ │ └── page.tsx
│ │ ├── components
│ │ │ └── AppLayout.tsx
│ │ └── theme.ts
│ └── tsconfig.json
│ ├── 4-3-nextjs-rsc-react-query-streaming
│ ├── .gitignore
│ ├── README.md
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.cjs
│ ├── public
│ │ ├── next.svg
│ │ └── vercel.svg
│ ├── src
│ │ ├── api-types.ts
│ │ ├── app
│ │ │ ├── ReactQueryProvider.tsx
│ │ │ ├── favicon.ico
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ └── posts
│ │ │ │ └── [id]
│ │ │ │ ├── CommentSection.tsx
│ │ │ │ ├── actions.tsx
│ │ │ │ ├── error.tsx
│ │ │ │ ├── loading.tsx
│ │ │ │ └── page.tsx
│ │ ├── components
│ │ │ └── AppLayout.tsx
│ │ └── theme.ts
│ └── tsconfig.json
│ └── 4-4-astro-server-islands
│ ├── .gitignore
│ ├── .vscode
│ ├── extensions.json
│ └── launch.json
│ ├── README.md
│ ├── astro.config.mjs
│ ├── package.json
│ ├── public
│ └── favicon.svg
│ ├── src
│ ├── actions
│ │ └── index.ts
│ ├── api-types.ts
│ ├── assets
│ │ ├── astro.svg
│ │ └── background.svg
│ ├── components
│ │ ├── Comments.astro
│ │ └── HomeFeed.astro
│ ├── layouts
│ │ └── Layout.astro
│ ├── pages
│ │ ├── index.astro
│ │ └── posts
│ │ │ └── [id].astro
│ └── styles
│ │ └── global.css
│ └── tsconfig.json
├── package.json
├── packages
└── types
│ ├── package.json
│ ├── src
│ └── types.ts
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── types
│ └── types.d.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── react-data-fetching-compressed.pdf
├── reactquery.pdf
└── turbo.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # Dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # Local env files
9 | .env
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | # Testing
16 | coverage
17 |
18 | # Turbo
19 | .turbo
20 |
21 | # Vercel
22 | .vercel
23 |
24 | # Build Outputs
25 | .next/
26 | out/
27 | build
28 | dist
29 |
30 |
31 | # Debug
32 | npm-debug.log*
33 | yarn-debug.log*
34 | yarn-error.log*
35 |
36 | # Misc
37 | .DS_Store
38 | *.pem
39 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/.npmrc
--------------------------------------------------------------------------------
/apps/0-json-server/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/0-json-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "0-json-server",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev0": "pnpm serve",
8 | "serve": "json-server --watch db.json --delay 400 --port 3300"
9 | },
10 | "dependencies": {
11 | "json-server": "0.17.4"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + componentDidMount
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1-1-vite-componentdidmount",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 3311",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "mantine-react-table": "^2.0.0-beta.6",
17 | "react": "^19.0.0",
18 | "react-dom": "^19.0.0",
19 | "react-router-dom": "^7.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^19.0.10",
23 | "@types/react-dom": "^19.0.4",
24 | "@typescript-eslint/eslint-plugin": "^8.24.1",
25 | "@typescript-eslint/parser": "^8.24.1",
26 | "@vitejs/plugin-react": "^4.3.4",
27 | "eslint": "^9.20.1",
28 | "eslint-plugin-react-hooks": "^5.1.0",
29 | "eslint-plugin-react-refresh": "^0.4.19",
30 | "postcss": "^8.5.3",
31 | "postcss-preset-mantine": "1.17.0",
32 | "postcss-simple-vars": "^7.0.1",
33 | "typescript": "^5.7.3",
34 | "vite": "^6.1.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { AppRoutes } from "./AppRoutes";
2 | import { AppProviders } from "./AppProviders";
3 | import "@mantine/core/styles.css";
4 |
5 | export const App = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
2 | import { useDisclosure } from "@mantine/hooks";
3 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
4 | import { Link } from "react-router-dom";
5 |
6 | interface AppLayoutProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const links = [
11 | {
12 | icon: ,
13 | color: "blue",
14 | label: "Home Feed",
15 | href: "/",
16 | },
17 | {
18 | icon: ,
19 | color: "teal",
20 | label: "Users",
21 | href: "/users",
22 | },
23 | ];
24 |
25 | export function AppLayout({ children }: AppLayoutProps) {
26 | const [opened, { toggle }] = useDisclosure();
27 |
28 | return (
29 |
34 |
35 |
36 |
37 | Vite with componentDidMount
38 |
39 |
40 |
41 | {links.map((link) => (
42 |
43 | {link.label}
44 |
45 | ))}
46 |
47 | {children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/AppProviders.tsx:
--------------------------------------------------------------------------------
1 | import { type ReactNode } from "react";
2 | import { MantineProvider } from "@mantine/core";
3 | import { BrowserRouter } from "react-router-dom";
4 | import { theme } from "./theme";
5 |
6 | interface Props {
7 | children: ReactNode;
8 | }
9 |
10 | export const AppProviders = ({ children }: Props) => {
11 | return (
12 |
13 | {children}
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/AppRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { AppLayout } from "./AppLayout";
3 | import { HomePage } from "./pages/HomePage";
4 | import PostPage from "./pages/PostPage";
5 | // import { UsersPage } from './pages/UsersPage';
6 | // import { UserPage } from './pages/UserPage';
7 |
8 | export const AppRoutes = () => {
9 | return (
10 |
11 |
12 | } />
13 | } />
14 | {/* } />
15 | } /> */}
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App.tsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")!).render(
6 |
7 |
8 | ,
9 | );
10 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/src/withRouter.tsx:
--------------------------------------------------------------------------------
1 | import { useLocation, useNavigate, useParams } from "react-router-dom";
2 |
3 | export default function withRouter(Component: any) {
4 | function ComponentWithRouterProp(props: any) {
5 | let location = useLocation();
6 | let navigate = useNavigate();
7 | let params = useParams();
8 | const router = { location, navigate, params };
9 | return ;
10 | }
11 | return ComponentWithRouterProp;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-1-vite-componentdidmount/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | ],
9 | ignorePatterns: ['dist', '.eslintrc.cjs'],
10 | parser: '@typescript-eslint/parser',
11 | plugins: ['react-refresh'],
12 | rules: {
13 | 'react-refresh/only-export-components': [
14 | 'warn',
15 | { allowConstantExport: true },
16 | ],
17 | },
18 | }
19 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + useEffect
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1-2-vite-useeffect",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 3312",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "mantine-react-table": "^2.0.0-beta.6",
17 | "react": "^19.0.0",
18 | "react-dom": "^19.0.0",
19 | "react-router-dom": "^7.2.0"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^19.0.10",
23 | "@types/react-dom": "^19.0.4",
24 | "@typescript-eslint/eslint-plugin": "^8.24.1",
25 | "@typescript-eslint/parser": "^8.24.1",
26 | "@vitejs/plugin-react": "^4.3.4",
27 | "eslint": "^9.20.1",
28 | "eslint-plugin-react-hooks": "^5.1.0",
29 | "eslint-plugin-react-refresh": "^0.4.19",
30 | "postcss": "^8.5.3",
31 | "postcss-preset-mantine": "1.17.0",
32 | "postcss-simple-vars": "^7.0.1",
33 | "typescript": "^5.7.3",
34 | "vite": "^6.1.1"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { AppRoutes } from "./AppRoutes";
2 | import { AppProviders } from "./AppProviders";
3 | import "@mantine/core/styles.css";
4 | import "mantine-react-table/styles.css";
5 |
6 | export const App = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/AppProviders.tsx:
--------------------------------------------------------------------------------
1 | import { type ReactNode } from "react";
2 | import { MantineProvider } from "@mantine/core";
3 | import { BrowserRouter } from "react-router-dom";
4 | import { theme } from "./theme";
5 |
6 | interface Props {
7 | children: ReactNode;
8 | }
9 |
10 | export const AppProviders = ({ children }: Props) => {
11 | return (
12 |
13 | {children}
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/AppRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { AppLayout } from "./AppLayout";
3 | import { HomePage } from "./pages/HomePage";
4 | import { PostPage } from "./pages/PostPage";
5 | import { UsersPage } from "./pages/UsersPage";
6 | import { UserPage } from "./pages/UserPage";
7 |
8 | export const AppRoutes = () => {
9 | return (
10 |
11 |
12 | } />
13 | } />
14 | } />
15 | } />
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App.tsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")!).render(
6 |
7 |
8 | ,
9 | );
10 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-2-vite-useeffect/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React Query
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1-3-vite-reactquery",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 3313",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "@tanstack/react-query": "^5.66.7",
17 | "@tanstack/react-query-devtools": "^5.66.7",
18 | "mantine-react-table": "^2.0.0-beta.6",
19 | "react": "^19.0.0",
20 | "react-dom": "^19.0.0",
21 | "react-router-dom": "^7.2.0"
22 | },
23 | "devDependencies": {
24 | "@types/react": "^19.0.10",
25 | "@types/react-dom": "^19.0.4",
26 | "@typescript-eslint/eslint-plugin": "^8.24.1",
27 | "@typescript-eslint/parser": "^8.24.1",
28 | "@vitejs/plugin-react": "^4.3.4",
29 | "eslint": "^9.20.1",
30 | "eslint-plugin-react-hooks": "^5.1.0",
31 | "eslint-plugin-react-refresh": "^0.4.19",
32 | "postcss": "^8.5.3",
33 | "postcss-preset-mantine": "1.17.0",
34 | "postcss-simple-vars": "^7.0.1",
35 | "typescript": "^5.7.3",
36 | "vite": "^6.1.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { AppRoutes } from "./AppRoutes";
2 | import { AppProviders } from "./AppProviders";
3 | import "@mantine/core/styles.css";
4 | import "mantine-react-table/styles.css";
5 |
6 | export const App = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/AppProviders.tsx:
--------------------------------------------------------------------------------
1 | import { lazy, Suspense, type ReactNode } from "react";
2 | import { MantineProvider } from "@mantine/core";
3 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4 | import { BrowserRouter } from "react-router-dom";
5 | import { theme } from "./theme";
6 |
7 | const ReactQueryDevtoolsProduction = lazy(() =>
8 | import("@tanstack/react-query-devtools/production").then((d) => ({
9 | default: d.ReactQueryDevtools,
10 | })),
11 | );
12 |
13 | const queryClient = new QueryClient({
14 | defaultOptions: {
15 | queries: {
16 | staleTime: 1000 * 10, // 10 seconds
17 | },
18 | },
19 | });
20 |
21 | interface Props {
22 | children: ReactNode;
23 | }
24 |
25 | export const AppProviders = ({ children }: Props) => {
26 | return (
27 |
28 |
29 |
30 | {children}
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/AppRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { AppLayout } from "./AppLayout";
3 | import { HomePage } from "./pages/HomePage";
4 | import { PostPage } from "./pages/PostPage";
5 | import { UsersPage } from "./pages/UsersPage";
6 | import { UserPage } from "./pages/UserPage";
7 |
8 | export const AppRoutes = () => {
9 | return (
10 |
11 |
12 | } />
13 | } />
14 | } />
15 | } />
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App.tsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")!).render(
6 |
7 |
8 | ,
9 | );
10 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-3-vite-reactquery/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React Query Hooks Refactor
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1-4-vite-reactquery-refactor",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 3314",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "@tanstack/react-query": "^5.66.7",
17 | "@tanstack/react-query-devtools": "^5.66.7",
18 | "mantine-react-table": "^2.0.0-beta.6",
19 | "react": "^19.0.0",
20 | "react-dom": "^19.0.0",
21 | "react-error-boundary": "^5.0.0",
22 | "react-router-dom": "^7.2.0"
23 | },
24 | "devDependencies": {
25 | "@types/react": "^19.0.10",
26 | "@types/react-dom": "^19.0.4",
27 | "@typescript-eslint/eslint-plugin": "^8.24.1",
28 | "@typescript-eslint/parser": "^8.24.1",
29 | "@vitejs/plugin-react": "^4.3.4",
30 | "eslint": "^9.20.1",
31 | "eslint-plugin-react-hooks": "^5.1.0",
32 | "eslint-plugin-react-refresh": "^0.4.19",
33 | "postcss": "^8.5.3",
34 | "postcss-preset-mantine": "1.17.0",
35 | "postcss-simple-vars": "^7.0.1",
36 | "typescript": "^5.7.3",
37 | "vite": "^6.1.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { AppRoutes } from "./AppRoutes";
2 | import { AppProviders } from "./AppProviders";
3 | import "@mantine/core/styles.css";
4 | import "mantine-react-table/styles.css";
5 |
6 | export const App = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/AppProviders.tsx:
--------------------------------------------------------------------------------
1 | import { lazy, Suspense, type ReactNode } from "react";
2 | import { LoadingOverlay, MantineProvider } from "@mantine/core";
3 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4 | import { BrowserRouter } from "react-router-dom";
5 | import { theme } from "./theme";
6 |
7 | const ReactQueryDevtoolsProduction = lazy(() =>
8 | import("@tanstack/react-query-devtools/production").then((d) => ({
9 | default: d.ReactQueryDevtools,
10 | })),
11 | );
12 |
13 | const queryClient = new QueryClient({
14 | defaultOptions: {
15 | queries: {
16 | staleTime: 1000 * 10, // 10 seconds
17 | },
18 | },
19 | });
20 |
21 | interface Props {
22 | children: ReactNode;
23 | }
24 |
25 | export const AppProviders = ({ children }: Props) => {
26 | return (
27 |
28 |
29 |
30 | }>{children}
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/AppRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { AppLayout } from "./AppLayout";
3 | import { HomePage } from "./pages/HomePage";
4 | import { PostPage } from "./pages/PostPage";
5 | import { UsersPage } from "./pages/UsersPage";
6 | import { UserPage } from "./pages/UserPage";
7 |
8 | export const AppRoutes = () => {
9 | return (
10 |
11 |
12 | } />
13 | } />
14 | } />
15 | } />
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetPost.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { IPost } from "../api-types";
3 |
4 | const API_URL = "http://localhost:3300";
5 | const ENDPOINT = (postId: string) => `/posts/${postId}`;
6 |
7 | export const getPostQueryKey = (postId: string) => [ENDPOINT(postId)];
8 |
9 | export function useGetPost(postId: string) {
10 | return useQuery({
11 | queryKey: getPostQueryKey(postId),
12 | queryFn: async () => {
13 | const response = await fetch(`${API_URL}${ENDPOINT(postId)}`);
14 | return response.json() as Promise;
15 | },
16 | });
17 | }
18 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetPostComments.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { IComment } from "../api-types";
3 |
4 | const API_URL = "http://localhost:3300";
5 | const ENDPOINT = (postId: string) => `/posts/${postId}/comments`;
6 |
7 | export const getPostCommentsQueryKey = (postId: string) => [ENDPOINT(postId)];
8 |
9 | export function useGetPostComments(postId: string) {
10 | return useQuery({
11 | queryKey: getPostCommentsQueryKey(postId),
12 | queryFn: async () => {
13 | const response = await fetch(`${API_URL}${ENDPOINT(postId)}`);
14 | return response.json() as Promise;
15 | },
16 | refetchInterval: 10000, // 10 seconds
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetPosts.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient, useSuspenseQuery } from "@tanstack/react-query";
2 | import { IPost } from "../api-types";
3 |
4 | const API_URL = "http://localhost:3300";
5 | const ENDPOINT = "/posts";
6 |
7 | export const getPostsQueryKey = () => [ENDPOINT];
8 |
9 | function commonOptions() {
10 | return {
11 | queryKey: getPostsQueryKey(),
12 | queryFn: async () => {
13 | const fetchUrl = new URL(`${API_URL}${ENDPOINT}`);
14 | const response = await fetch(fetchUrl.href);
15 | return response.json() as Promise;
16 | },
17 | };
18 | }
19 |
20 | export function useGetPosts() {
21 | return useSuspenseQuery(commonOptions());
22 | }
23 |
24 | export function prefetchPosts(queryClient: QueryClient) {
25 | queryClient.prefetchQuery(commonOptions());
26 | }
27 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetUser.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient, useQuery } from "@tanstack/react-query";
2 | import { IUser } from "../api-types";
3 |
4 | const API_URL = "http://localhost:3300";
5 | const ENDPOINT = (userId: number) => `/users/${userId}`;
6 |
7 | export const getUserQueryKey = (userId: number) => [ENDPOINT(userId)];
8 |
9 | function commonOptions(userId?: number) {
10 | return {
11 | queryKey: getUserQueryKey(userId!),
12 | queryFn: async () => {
13 | const response = await fetch(`${API_URL}${ENDPOINT(userId!)}`);
14 | return response.json() as Promise;
15 | },
16 | };
17 | }
18 |
19 | export function useGetUser(userId?: number) {
20 | return useQuery({
21 | ...commonOptions(userId),
22 | enabled: !!userId,
23 | });
24 | }
25 |
26 | export function prefetchUser(queryClient: QueryClient, userId: number) {
27 | queryClient.prefetchQuery(commonOptions(userId));
28 | }
29 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetUserPosts.ts:
--------------------------------------------------------------------------------
1 | import { useQuery, useQueryClient } from "@tanstack/react-query";
2 | import { IPost } from "../api-types";
3 | import { getPostsQueryKey } from "./useGetPosts";
4 |
5 | const API_URL = "http://localhost:3300";
6 | const ENDPOINT = (userId: number) => `/posts?userId=${userId}`;
7 |
8 | export const getUserPostsQueryKey = (userId: number) => [ENDPOINT(userId)];
9 |
10 | export const useGetUserPosts = (userId: number) => {
11 | const queryClient = useQueryClient();
12 |
13 | return useQuery({
14 | initialData: () =>
15 | queryClient
16 | .getQueryData(getPostsQueryKey())
17 | ?.filter((post) => post.userId === userId),
18 | queryKey: getUserPostsQueryKey(userId),
19 | queryFn: async () => {
20 | const fetchUrl = new URL(`${API_URL}${ENDPOINT(userId)}`);
21 | const response = await fetch(fetchUrl.href);
22 | return response.json() as Promise;
23 | },
24 | });
25 | };
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/hooks/useGetUsers.ts:
--------------------------------------------------------------------------------
1 | import { QueryClient, useQuery } from "@tanstack/react-query";
2 | import { IUser } from "../api-types";
3 |
4 | const API_URL = "http://localhost:3300";
5 | const ENDPOINT = "/users";
6 |
7 | export const getUsersQueryKey = () => [ENDPOINT];
8 |
9 | function commonOptions() {
10 | return {
11 | queryKey: getUsersQueryKey(),
12 | queryFn: async () => {
13 | const response = await fetch(`${API_URL}${ENDPOINT}`);
14 | return response.json() as Promise;
15 | },
16 | };
17 | }
18 |
19 | export function useGetUsers() {
20 | return useQuery(commonOptions());
21 | }
22 |
23 | export function prefetchUsers(queryClient: QueryClient) {
24 | queryClient.prefetchQuery(commonOptions());
25 | }
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { App } from "./App.tsx";
4 |
5 | ReactDOM.createRoot(document.getElementById("root")!).render(
6 |
7 |
8 | ,
9 | );
10 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true,
8 | "strict": true
9 | },
10 | "include": ["vite.config.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-4-vite-reactquery-refactor/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from 'astro/config';
3 |
4 | import tailwindcss from '@tailwindcss/vite';
5 |
6 | import react from '@astrojs/react';
7 |
8 | // https://astro.build/config
9 | export default defineConfig({
10 | vite: {
11 | plugins: [tailwindcss()]
12 | },
13 |
14 | integrations: [react()]
15 | });
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "1-5-astro-react-spa",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev --port 3315",
7 | "build": "astro build",
8 | "preview": "astro preview --port 3315",
9 | "astro": "astro"
10 | },
11 | "dependencies": {
12 | "@astrojs/react": "^4.2.0",
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "@tailwindcss/vite": "^4.0.7",
17 | "@tanstack/react-query": "^5.66.7",
18 | "@tanstack/react-query-devtools": "^5.66.7",
19 | "@types/react-dom": "^19.0.4",
20 | "astro": "^5.3.0",
21 | "mantine-react-table": "2.0.0-beta.8",
22 | "react": "^19.0.0",
23 | "react-dom": "^19.0.0",
24 | "react-router-dom": "^7.2.0",
25 | "tailwindcss": "^4.0.7"
26 | },
27 | "devDependencies": {
28 | "@types/react": "^19.0.10",
29 | "postcss": "^8.5.3",
30 | "postcss-preset-mantine": "1.17.0",
31 | "postcss-simple-vars": "^7.0.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/layouts/Layout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import "../styles/global.css";
3 | ---
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Astro React SPA
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
30 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../layouts/Layout.astro";---
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Static Astro Page
10 |
11 | This is a static page built with Astro. Click below to visit the SPA
12 | portal.
13 |
14 |
18 | Go to SPA Portal
19 |
20 |
21 |
22 |
25 |
26 |
Static Page
27 |
28 | This page is statically rendered at build time, providing optimal
29 | performance and SEO benefits.
30 |
31 |
32 |
33 |
34 |
SPA Portal
35 |
36 | The SPA portal demonstrates client-side rendering with React for
37 | dynamic interactivity.
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/pages/spa/[...slug].astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../../layouts/Layout.astro";
3 | import { App } from "../../spa/App";
4 | import type { IPost, IUser } from "../../spa/api-types";
5 |
6 | export async function getStaticPaths() {
7 | const users = await fetch("http://localhost:3300/users");
8 | const usersData = (await users.json()) as IUser[];
9 | const posts = await fetch("http://localhost:3300/posts");
10 | const postsData = (await posts.json()) as IPost[];
11 |
12 | const paths = [
13 | { params: { slug: undefined } },
14 | { params: { slug: "users" } },
15 | ...usersData.map((user) => ({ params: { slug: `users/${user.id}` } })),
16 | ...postsData.map((post) => ({ params: { slug: `posts/${post.id}` } })),
17 | ];
18 |
19 | return paths;
20 | }
21 |
22 | export const prerender = true;
23 | ---
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/spa/App.tsx:
--------------------------------------------------------------------------------
1 | import { AppRoutes } from "./AppRoutes";
2 | import { AppProviders } from "./AppProviders";
3 | import "@mantine/core/styles.css";
4 | import "mantine-react-table/styles.css";
5 |
6 | export const App = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/spa/AppProviders.tsx:
--------------------------------------------------------------------------------
1 | import { lazy, Suspense, type ReactNode } from "react";
2 | import {
3 | ColorSchemeScript,
4 | LoadingOverlay,
5 | MantineProvider,
6 | } from "@mantine/core";
7 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8 | import { BrowserRouter } from "react-router-dom";
9 | import { theme } from "./theme";
10 |
11 | const ReactQueryDevtoolsProduction = lazy(() =>
12 | import("@tanstack/react-query-devtools/production").then((d) => ({
13 | default: d.ReactQueryDevtools,
14 | }))
15 | );
16 |
17 | const queryClient = new QueryClient({
18 | defaultOptions: {
19 | queries: {
20 | staleTime: 1000 * 10, // 10 seconds
21 | },
22 | },
23 | });
24 |
25 | interface Props {
26 | children: ReactNode;
27 | }
28 |
29 | export const AppProviders = ({ children }: Props) => {
30 | return (
31 |
32 |
33 |
34 |
35 | }>{children}
36 |
37 |
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/spa/AppRoutes.tsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { AppLayout } from "./AppLayout";
3 | import { HomePage } from "./pages/HomePage";
4 | import { PostPage } from "./pages/PostPage";
5 | import { UsersPage } from "./pages/UsersPage";
6 | import { UserPage } from "./pages/UserPage";
7 |
8 | export const AppRoutes = () => {
9 | return (
10 |
11 |
12 |
13 | } />
14 | } />
15 | } />
16 | } />
17 |
18 |
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/spa/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/spa/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 | /* @import "@mantine/core/styles.css"; */
3 |
--------------------------------------------------------------------------------
/apps/1-spa/1-5-astro-react-spa/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "include": [
4 | ".astro/types.d.ts",
5 | "**/*"
6 | ],
7 | "exclude": [
8 | "dist"
9 | ],
10 | "compilerOptions": {
11 | "jsx": "react-jsx",
12 | "jsxImportSource": "react"
13 | }
14 | }
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
20 |
21 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
22 |
23 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
24 |
25 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
26 |
27 | ## Learn More
28 |
29 | To learn more about Next.js, take a look at the following resources:
30 |
31 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
32 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
33 |
34 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
35 |
36 | ## Deploy on Vercel
37 |
38 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
39 |
40 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
41 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "2-1-nextjs-ssg",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev -p 3321",
7 | "build": "next build",
8 | "start": "next start -p 3321",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mantine/core": "7.17.0",
13 | "@mantine/hooks": "7.17.0",
14 | "@tabler/icons-react": "3.17.0",
15 | "@tanstack/react-query": "^5.66.7",
16 | "mantine-react-table": "^2.0.0-beta.6",
17 | "next": "15.1.7",
18 | "react": "^19",
19 | "react-dom": "^19"
20 | },
21 | "devDependencies": {
22 | "@tanstack/react-query-devtools": "^5.66.7",
23 | "@types/node": "^22",
24 | "@types/react": "^19",
25 | "@types/react-dom": "^19",
26 | "postcss": "^8.5.3",
27 | "postcss-preset-mantine": "1.17.0",
28 | "postcss-simple-vars": "^7.0.1",
29 | "typescript": "^5"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/2-ssg/2-1-nextjs-ssg/public/favicon.ico
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
2 | import { useDisclosure } from "@mantine/hooks";
3 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
4 | import Link from "next/link";
5 |
6 | interface AppLayoutProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const links = [
11 | {
12 | icon: ,
13 | color: "blue",
14 | label: "Home Feed",
15 | href: "/",
16 | },
17 | {
18 | icon: ,
19 | color: "teal",
20 | label: "Users",
21 | href: "/users",
22 | },
23 | ];
24 |
25 | export function AppLayout({ children }: AppLayoutProps) {
26 | const [opened, { toggle }] = useDisclosure();
27 |
28 | return (
29 |
34 |
35 |
36 |
37 | Next JS SSG
38 |
39 |
40 |
41 | {links.map((link) => (
42 |
43 | {link.label}
44 |
45 | ))}
46 |
47 | {children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import type { AppProps } from "next/app";
3 | import Head from "next/head";
4 | import { MantineProvider } from "@mantine/core";
5 | import { theme } from "@/theme";
6 | import { AppLayout } from "@/components/AppLayout";
7 | import { useState } from "react";
8 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
9 |
10 | export default function App({ Component, pageProps }: AppProps) {
11 | const [queryClient] = useState(
12 | () =>
13 | new QueryClient({
14 | defaultOptions: {
15 | queries: {
16 | staleTime: 1000 * 10, // 10 seconds
17 | },
18 | },
19 | }),
20 | );
21 |
22 | return (
23 | <>
24 |
25 | Next JS SSG
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | >
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 | import { ColorSchemeScript } from "@mantine/core";
3 |
4 | export default function Document() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 |
4 | type Data = {
5 | name: string;
6 | };
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse,
11 | ) {
12 | res.status(200).json({ name: "John Doe" });
13 | }
14 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { GetStaticProps } from "next";
2 | import { Card, Flex, Stack, Text, Title } from "@mantine/core";
3 | import Link from "next/link";
4 | import { type IPost } from "../api-types";
5 |
6 | //Load posts at build time for SSG
7 | export const getStaticProps: GetStaticProps = async () => {
8 | try {
9 | const fetchUrl = new URL(`http://localhost:3300/posts`);
10 | const response = await fetch(fetchUrl.href);
11 | const fetchedPosts = (await response.json()) as IPost[];
12 |
13 | return {
14 | props: {
15 | posts: fetchedPosts,
16 | error: false,
17 | },
18 | };
19 | } catch (error) {
20 | console.error(error);
21 |
22 | return {
23 | props: {
24 | posts: [],
25 | error: true,
26 | },
27 | revalidate: 10, // Optionally revalidate the data every 10 seconds
28 | };
29 | }
30 | };
31 |
32 | interface HomePageProps {
33 | posts: IPost[];
34 | error: boolean;
35 | }
36 |
37 | export default function HomePage({ posts, error }: HomePageProps) {
38 | return (
39 |
40 | Your Home Feed
41 |
42 | {posts?.map((post) => (
43 |
48 |
57 | {post.title}
58 | {post.body}
59 |
60 | Go to post
61 |
62 |
63 |
64 | ))}
65 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-1-nextjs-ssg/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "paths": {
20 | "@/*": [
21 | "./src/*"
22 | ]
23 | },
24 | "target": "ES2017"
25 | },
26 | "include": [
27 | "next-env.d.ts",
28 | "**/*.ts",
29 | "**/*.tsx"
30 | ],
31 | "exclude": [
32 | "node_modules"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from "astro/config";
3 |
4 | import tailwindcss from "@tailwindcss/vite";
5 |
6 | import vercel from "@astrojs/vercel";
7 |
8 | // https://astro.build/config
9 | export default defineConfig({
10 | vite: {
11 | plugins: [tailwindcss()],
12 | },
13 | adapter: vercel({
14 | isr: {
15 | expiration: 60 * 60 * 24, // rebuild every page every day
16 | },
17 | }),
18 | });
19 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "2-2-astro-ssg",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev --port 3322",
7 | "build": "astro build",
8 | "preview": "astro preview --port 3322",
9 | "astro": "astro"
10 | },
11 | "dependencies": {
12 | "@astrojs/vercel": "^8.0.8",
13 | "@tailwindcss/vite": "^4.0.7",
14 | "astro": "^5.3.0",
15 | "tailwindcss": "^4.0.7"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../layouts/Layout.astro";
3 | import type { IPost } from "../api-types";
4 |
5 | const fetchPosts = async (): Promise<{ posts: IPost[]; error: boolean }> => {
6 | try {
7 | const response = await fetch("http://localhost:3300/posts");
8 | const fetchedPosts = (await response.json()) as IPost[];
9 | return { posts: fetchedPosts, error: false };
10 | } catch (error) {
11 | console.error(error);
12 | return { posts: [], error: true };
13 | }
14 | };
15 |
16 | const { posts, error } = await fetchPosts();
17 | ---
18 |
19 |
20 |
21 |
Your Home Feed
22 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
--------------------------------------------------------------------------------
/apps/2-ssg/2-2-astro-ssg/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "include": [".astro/types.d.ts", "**/*"],
4 | "exclude": ["dist"]
5 | }
6 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from "astro/config";
3 |
4 | import react from "@astrojs/react";
5 |
6 | // https://astro.build/config
7 | export default defineConfig({
8 | integrations: [react()],
9 | });
10 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "2-3-astro-react-ssg",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev --port 3323",
7 | "build": "astro build",
8 | "preview": "astro preview --port 3323",
9 | "astro": "astro"
10 | },
11 | "dependencies": {
12 | "@astrojs/react": "^4.2.0",
13 | "@mantine/core": "7.17.0",
14 | "@mantine/hooks": "7.17.0",
15 | "@tabler/icons-react": "3.17.0",
16 | "@tailwindcss/vite": "^4.0.7",
17 | "astro": "^5.3.0",
18 | "mantine-react-table": "2.0.0-beta.8",
19 | "react": "^19.0.0",
20 | "react-dom": "^19.0.0"
21 | },
22 | "devDependencies": {
23 | "postcss": "^8.5.3",
24 | "postcss-preset-mantine": "1.17.0",
25 | "postcss-simple-vars": "^7.0.1",
26 | "@types/react": "^19.0.10",
27 | "@types/react-dom": "^19.0.4"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
2 | import { useDisclosure } from "@mantine/hooks";
3 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
4 | import ReactProviders from "./ReactProviders";
5 |
6 | interface AppLayoutProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const links = [
11 | {
12 | icon: ,
13 | color: "blue",
14 | label: "Home Feed",
15 | href: "/",
16 | },
17 | {
18 | icon: ,
19 | color: "teal",
20 | label: "Users",
21 | href: "/users",
22 | },
23 | ];
24 |
25 | export function AppLayout({ children }: AppLayoutProps) {
26 | const [opened, { toggle }] = useDisclosure();
27 |
28 | return (
29 |
30 |
39 |
40 |
41 |
42 | Astro React SSG
43 |
44 |
45 |
46 | {links.map((link) => (
47 |
48 | {link.label}
49 |
50 | ))}
51 |
52 |
53 | {children}
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/components/HomePage.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, Card, Title, Text, Stack, Flex } from "@mantine/core";
2 | import type { IPost } from "../api-types";
3 | import ReactProviders from "./ReactProviders";
4 |
5 | interface HomePageProps {
6 | posts: IPost[];
7 | error: boolean;
8 | }
9 |
10 | export function HomePage({ posts, error }: HomePageProps) {
11 | return (
12 |
13 |
14 | Your Home Feed
15 |
16 | {posts?.map((post) => (
17 |
22 |
31 | {post.title}
32 | {post.body}
33 |
34 | Go to post
35 |
36 |
37 |
38 | ))}
39 |
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/components/ReactProviders.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import { MantineProvider } from "@mantine/core";
3 | import { theme } from "../theme";
4 | import { ColorSchemeScript } from "@mantine/core";
5 |
6 | export default function ReactProviders({
7 | children,
8 | }: {
9 | children: React.ReactNode;
10 | }) {
11 | return (
12 |
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/layouts/Layout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | // import "./src/styles/global.css";
3 | import { AppLayout } from "../components/AppLayout";
4 | ---
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Astro SSG
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../layouts/Layout.astro";
3 | import type { IPost } from "../api-types";
4 | import { HomePage } from "../components/HomePage";
5 |
6 | const fetchPosts = async (): Promise<{ posts: IPost[]; error: boolean }> => {
7 | try {
8 | const response = await fetch("http://localhost:3300/posts");
9 | const fetchedPosts = (await response.json()) as IPost[];
10 | return { posts: fetchedPosts, error: false };
11 | } catch (error) {
12 | console.error(error);
13 | return { posts: [], error: true };
14 | }
15 | };
16 |
17 | const { posts, error } = await fetchPosts();
18 | ---
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/pages/posts/[id].astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../../layouts/Layout.astro";
3 | import { type IComment, type IPost, type IUser } from "../../api-types";
4 | import PostPage from "../../components/PostPage";
5 |
6 | export async function getStaticPaths() {
7 | const response = await fetch(`http://localhost:3300/posts`);
8 | const posts = (await response.json()) as IPost[];
9 |
10 | return posts.map((post) => ({
11 | params: { id: post.id.toString() },
12 | }));
13 | }
14 |
15 | const { id } = Astro.params;
16 |
17 | let post: IPost;
18 | let comments: IComment[];
19 | let user: IUser;
20 | let error: boolean = false;
21 | try {
22 | const [postResponse, commentsResponse] = await Promise.all([
23 | fetch(`http://localhost:3300/posts/${id}`),
24 | fetch(`http://localhost:3300/posts/${id}/comments`),
25 | ]);
26 |
27 | [post, comments] = (await Promise.all([
28 | postResponse.json(),
29 | commentsResponse.json(),
30 | ])) as [IPost, IComment[]];
31 |
32 | const userResponse = await fetch(
33 | `http://localhost:3300/users/${post.userId}`
34 | );
35 | user = (await userResponse.json()) as IUser;
36 | } catch (error) {
37 | console.error(error);
38 | error = true;
39 | return Astro.redirect("/error");
40 | }
41 | ---
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import "@mantine/core/styles.css";
2 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/2-ssg/2-3-astro-react-ssg/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "include": [
4 | ".astro/types.d.ts",
5 | "**/*"
6 | ],
7 | "exclude": [
8 | "dist"
9 | ],
10 | "compilerOptions": {
11 | "jsx": "react-jsx",
12 | "jsxImportSource": "react"
13 | }
14 | }
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
20 |
21 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
22 |
23 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
24 |
25 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
26 |
27 | ## Learn More
28 |
29 | To learn more about Next.js, take a look at the following resources:
30 |
31 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
32 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
33 |
34 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
35 |
36 | ## Deploy on Vercel
37 |
38 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
39 |
40 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
41 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3-1-nextjs-ssr",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev -p 3331",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mantine/core": "7.17.0",
13 | "@mantine/hooks": "7.17.0",
14 | "@tabler/icons-react": "3.17.0",
15 | "mantine-react-table": "^2.0.0-beta.6",
16 | "next": "15.1.7",
17 | "react": "^19",
18 | "react-dom": "^19"
19 | },
20 | "devDependencies": {
21 | "@tanstack/react-query": "^5.66.7",
22 | "@tanstack/react-query-devtools": "^5.66.7",
23 | "@types/node": "^22",
24 | "@types/react": "^19",
25 | "@types/react-dom": "^19",
26 | "postcss": "^8.5.3",
27 | "postcss-preset-mantine": "1.17.0",
28 | "postcss-simple-vars": "^7.0.1",
29 | "typescript": "^5"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/3-ssr/3-1-nextjs-ssr/public/favicon.ico
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
2 | import { useDisclosure } from "@mantine/hooks";
3 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
4 | import Link from "next/link";
5 |
6 | interface AppLayoutProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const links = [
11 | {
12 | icon: ,
13 | color: "blue",
14 | label: "Home Feed",
15 | href: "/",
16 | },
17 | {
18 | icon: ,
19 | color: "teal",
20 | label: "Users",
21 | href: "/users",
22 | },
23 | ];
24 |
25 | export function AppLayout({ children }: AppLayoutProps) {
26 | const [opened, { toggle }] = useDisclosure();
27 |
28 | return (
29 |
34 |
35 |
36 |
37 | Next JS SSR (and React Query)
38 |
39 |
40 |
41 | {links.map((link) => (
42 |
43 | {link.label}
44 |
45 | ))}
46 |
47 | {children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import { Suspense, useState } from "react";
3 | import type { AppProps } from "next/app";
4 | import Head from "next/head";
5 | import { MantineProvider } from "@mantine/core";
6 | import { theme } from "@/theme";
7 | import { AppLayout } from "@/components/AppLayout";
8 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
9 | import dynamic from "next/dynamic";
10 |
11 | const ReactQueryDevtoolsProduction = dynamic(() =>
12 | import("@tanstack/react-query-devtools/production").then((d) => ({
13 | default: d.ReactQueryDevtools,
14 | })),
15 | );
16 |
17 | export default function App({ Component, pageProps }: AppProps) {
18 | const [queryClient] = useState(
19 | () =>
20 | new QueryClient({
21 | defaultOptions: {
22 | queries: {
23 | staleTime: 1000 * 10, // 10 seconds
24 | },
25 | },
26 | }),
27 | );
28 |
29 | return (
30 | <>
31 |
32 | Next JS SSR (and React Query)
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | >
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from "next/document";
2 | import { ColorSchemeScript } from "@mantine/core";
3 |
4 | export default function Document() {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from "next";
3 |
4 | type Data = {
5 | name: string;
6 | };
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse,
11 | ) {
12 | res.status(200).json({ name: "John Doe" });
13 | }
14 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/src/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-1-nextjs-ssr/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "paths": {
20 | "@/*": [
21 | "./src/*"
22 | ]
23 | },
24 | "target": "ES2017"
25 | },
26 | "include": [
27 | "next-env.d.ts",
28 | "**/*.ts",
29 | "**/*.tsx"
30 | ],
31 | "exclude": [
32 | "node_modules"
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | /.cache
4 | /build
5 | .env
6 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to Remix!
2 |
3 | - 📖 [Remix docs](https://remix.run/docs)
4 |
5 | ## Development
6 |
7 | Run the dev server:
8 |
9 | ```shellscript
10 | npm run dev
11 | ```
12 |
13 | ## Deployment
14 |
15 | First, build your app for production:
16 |
17 | ```sh
18 | npm run build
19 | ```
20 |
21 | Then run the app in production mode:
22 |
23 | ```sh
24 | npm start
25 | ```
26 |
27 | Now you'll need to pick a host to deploy it to.
28 |
29 | ### DIY
30 |
31 | If you're familiar with deploying Node applications, the built-in Remix app server is production-ready.
32 |
33 | Make sure to deploy the output of `npm run build`
34 |
35 | - `build/server`
36 | - `build/client`
37 |
38 | ## Styling
39 |
40 | This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever css framework you prefer. See the [Vite docs on css](https://vitejs.dev/guide/features.html#css) for more information.
41 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/app/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/app/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
2 | import { useDisclosure } from "@mantine/hooks";
3 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
4 | import { Link } from "@remix-run/react";
5 |
6 | interface AppLayoutProps {
7 | children: React.ReactNode;
8 | }
9 |
10 | const links = [
11 | {
12 | icon: ,
13 | color: "blue",
14 | label: "Home Feed",
15 | href: "/",
16 | },
17 | {
18 | icon: ,
19 | color: "teal",
20 | label: "Users",
21 | href: "/users",
22 | },
23 | ];
24 |
25 | export function AppLayout({ children }: AppLayoutProps) {
26 | const [opened, { toggle }] = useDisclosure();
27 |
28 | return (
29 |
34 |
35 |
36 |
37 | Remix SSR (and React Query)
38 |
39 |
40 |
41 | {links.map((link) => (
42 |
43 | {link.label}
44 |
45 | ))}
46 |
47 | {children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/app/entry.client.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * By default, Remix will handle hydrating your app on the client for you.
3 | * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4 | * For more information, see https://remix.run/file-conventions/entry.client
5 | */
6 |
7 | import { RemixBrowser } from "@remix-run/react";
8 | import { startTransition, StrictMode } from "react";
9 | import { hydrateRoot } from "react-dom/client";
10 |
11 | startTransition(() => {
12 | hydrateRoot(
13 | document,
14 |
15 |
16 | ,
17 | );
18 | });
19 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/app/root.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import {
3 | Links,
4 | Meta,
5 | Outlet,
6 | Scripts,
7 | ScrollRestoration,
8 | } from "@remix-run/react";
9 | import { MantineProvider, ColorSchemeScript } from "@mantine/core";
10 | import { AppLayout } from "./components/AppLayout";
11 | import { lazy, ReactNode, Suspense, useState } from "react";
12 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
13 |
14 | const ReactQueryDevtoolsProduction = lazy(() =>
15 | import("@tanstack/react-query-devtools/production").then((d) => ({
16 | default: d.ReactQueryDevtools,
17 | })),
18 | );
19 |
20 | export function Layout({ children }: { children: ReactNode }) {
21 | const [queryClient] = useState(
22 | () =>
23 | new QueryClient({
24 | defaultOptions: {
25 | queries: {
26 | staleTime: 1000 * 10, // 10 seconds
27 | },
28 | },
29 | }),
30 | );
31 |
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {children}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | export default function App() {
58 | return ;
59 | }
60 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/app/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme } from "@mantine/core";
2 |
3 | export const theme = createTheme({
4 | /* Put your mantine theme override here */
5 | });
6 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3-2-remix-ssr",
3 | "private": true,
4 | "sideEffects": false,
5 | "type": "module",
6 | "scripts": {
7 | "build": "remix vite:build",
8 | "dev": "remix vite:dev --port 3332",
9 | "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
10 | "start": "remix-serve ./build/server/index.js",
11 | "typecheck": "tsc"
12 | },
13 | "dependencies": {
14 | "@mantine/core": "7.17.0",
15 | "@mantine/hooks": "7.17.0",
16 | "@remix-run/node": "^2.15.3",
17 | "@remix-run/react": "^2.15.3",
18 | "@remix-run/serve": "^2.15.3",
19 | "@tabler/icons-react": "3.17.0",
20 | "@tanstack/react-query": "^5.66.7",
21 | "isbot": "^5.1.23",
22 | "mantine-react-table": "^2.0.0-beta.6",
23 | "react": "^19.0.0",
24 | "react-dom": "^19.0.0"
25 | },
26 | "devDependencies": {
27 | "@remix-run/dev": "^2.15.3",
28 | "@tanstack/react-query-devtools": "^5.66.7",
29 | "@types/react": "^19.0.10",
30 | "@types/react-dom": "^19.0.4",
31 | "@typescript-eslint/eslint-plugin": "^8.24.1",
32 | "@typescript-eslint/parser": "^8.24.1",
33 | "postcss": "^8.5.3",
34 | "postcss-preset-mantine": "1.17.0",
35 | "postcss-simple-vars": "^7.0.1",
36 | "typescript": "^5.7.3",
37 | "vite": "^6.1.1",
38 | "vite-tsconfig-paths": "^5.1.4"
39 | },
40 | "engines": {
41 | "node": ">=20.0.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/3-ssr/3-2-remix-ssr/public/favicon.ico
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "**/*.ts",
4 | "**/*.tsx",
5 | "**/.server/**/*.ts",
6 | "**/.server/**/*.tsx",
7 | "**/.client/**/*.ts",
8 | "**/.client/**/*.tsx"
9 | ],
10 | "compilerOptions": {
11 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
12 | "types": ["@remix-run/node", "vite/client"],
13 | "isolatedModules": true,
14 | "esModuleInterop": true,
15 | "jsx": "react-jsx",
16 | "module": "ESNext",
17 | "moduleResolution": "Bundler",
18 | "resolveJsonModule": true,
19 | "target": "ES2022",
20 | "strict": true,
21 | "allowJs": true,
22 | "skipLibCheck": true,
23 | "forceConsistentCasingInFileNames": true,
24 | "baseUrl": ".",
25 | "paths": {
26 | "~/*": ["./app/*"]
27 | },
28 |
29 | // Vite takes care of building everything, not tsc.
30 | "noEmit": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-2-remix-ssr/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { vitePlugin as remix } from "@remix-run/dev";
2 | import { defineConfig } from "vite";
3 | import tsconfigPaths from "vite-tsconfig-paths";
4 |
5 | export default defineConfig({
6 | plugins: [
7 | remix({
8 | future: {
9 | v3_fetcherPersist: true,
10 | v3_relativeSplatPath: true,
11 | v3_throwAbortReason: true,
12 | },
13 | }),
14 | tsconfigPaths(),
15 | ],
16 | });
17 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # Output
4 | .output
5 | .vercel
6 | /.svelte-kit
7 | /build
8 |
9 | # OS
10 | .DS_Store
11 | Thumbs.db
12 |
13 | # Env
14 | .env
15 | .env.*
16 | !.env.example
17 | !.env.test
18 |
19 | # Vite
20 | vite.config.js.timestamp-*
21 | vite.config.ts.timestamp-*
22 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/.prettierignore:
--------------------------------------------------------------------------------
1 | # Package Managers
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "overrides": [
8 | {
9 | "files": "*.svelte",
10 | "options": {
11 | "parser": "svelte"
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/README.md:
--------------------------------------------------------------------------------
1 | # create-svelte
2 |
3 | Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
4 |
5 | ## Creating a project
6 |
7 | If you're seeing this, you've probably already done this step. Congrats!
8 |
9 | ```bash
10 | # create a new project in the current directory
11 | npm create svelte@latest
12 |
13 | # create a new project in my-app
14 | npm create svelte@latest my-app
15 | ```
16 |
17 | ## Developing
18 |
19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20 |
21 | ```bash
22 | npm run dev
23 |
24 | # or start the server and open the app in a new browser tab
25 | npm run dev -- --open
26 | ```
27 |
28 | ## Building
29 |
30 | To create a production version of your app:
31 |
32 | ```bash
33 | npm run build
34 | ```
35 |
36 | You can preview the production build with `npm run preview`.
37 |
38 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
39 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3-3-sveltekit-ssr",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite dev --port 3333",
7 | "build": "vite build",
8 | "preview": "vite preview",
9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
11 | "lint": "prettier --check .",
12 | "format": "prettier --write ."
13 | },
14 | "devDependencies": {
15 | "@sveltejs/adapter-auto": "^4.0.0",
16 | "@sveltejs/kit": "^2.17.2",
17 | "@sveltejs/vite-plugin-svelte": "^5.0.3",
18 | "@tanstack/svelte-query": "^5.66.4",
19 | "@tanstack/svelte-query-devtools": "^5.66.4",
20 | "autoprefixer": "^10.4.20",
21 | "flowbite": "^3.1.2",
22 | "flowbite-svelte": "^0.47.4",
23 | "flowbite-svelte-icons": "^2.0.2",
24 | "prettier": "^3.5.1",
25 | "prettier-plugin-svelte": "^3.3.3",
26 | "prettier-plugin-tailwindcss": "^0.6.11",
27 | "svelte": "^5.20.2",
28 | "svelte-check": "^4.1.4",
29 | "tailwindcss": "^3.4.17",
30 | "typescript": "^5.7.3",
31 | "vite": "^6.1.1"
32 | },
33 | "type": "module"
34 | }
35 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/app.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss/base';
2 | @import 'tailwindcss/components';
3 | @import 'tailwindcss/utilities';
4 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | declare global {
4 | namespace App {
5 | // interface Error {}
6 | // interface Locals {}
7 | // interface PageData {}
8 | // interface PageState {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/lib/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/lib/components/AppLayout.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 | SvelteKit SSR (and Svelte Query)
15 |
16 |
17 |
18 |
19 | {#each links as link}
20 |
21 |
22 | {link.label}
23 |
24 | {/each}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | // place files you want to import through the `$lib` alias in this folder.
2 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 | SvelteKit SSR (and Svelte Query)
29 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {#if browser && import.meta.env.DEV && SvelteQueryDevtools}
41 |
42 | {/if}
43 |
44 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import type { IPost } from '$lib/api-types';
2 | import type { PageServerLoad } from './$types';
3 |
4 | export const load: PageServerLoad = async ({ fetch }) => {
5 | try {
6 | const fetchUrl = new URL(`http://localhost:3300/posts`);
7 | const response = await fetch(fetchUrl.href);
8 | const fetchedPosts = (await response.json()) as IPost[];
9 |
10 | return {
11 | posts: fetchedPosts,
12 | error: false
13 | };
14 | } catch (error) {
15 | console.error(error);
16 | return {
17 | posts: [],
18 | error: true
19 | };
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
Your Home Feed
11 |
29 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/src/routes/posts/[id]/+page.server.ts:
--------------------------------------------------------------------------------
1 | import type { IComment, IPost } from '$lib/api-types';
2 | import type { PageServerLoad } from './$types';
3 |
4 | export const load: PageServerLoad = async ({ params, fetch }) => {
5 | const { id: postId } = params;
6 |
7 | try {
8 | const [postResponse, commentsResponse] = await Promise.all([
9 | fetch(`http://localhost:3300/posts/${postId}`),
10 | fetch(`http://localhost:3300/posts/${postId}/comments`)
11 | ]);
12 |
13 | const [initialPost, initialComments] = await Promise.all([
14 | postResponse.json() as Promise,
15 | commentsResponse.json() as Promise
16 | ]);
17 |
18 | return {
19 | initialPost,
20 | initialComments,
21 | error: false
22 | };
23 | } catch (error) {
24 | console.error(error);
25 |
26 | return {
27 | initialPost: null,
28 | initialComments: [],
29 | error: true
30 | };
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/3-ssr/3-3-sveltekit-ssr/static/favicon.png
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters.
14 | adapter: adapter()
15 | }
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import flowbitePlugin from 'flowbite/plugin';
2 |
3 | import type { Config } from 'tailwindcss';
4 |
5 | export default {
6 | content: [
7 | './src/**/*.{html,js,svelte,ts}',
8 | './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'
9 | ],
10 | darkMode: 'selector',
11 | theme: {
12 | extend: {
13 | colors: {
14 | // flowbite-svelte
15 | primary: {
16 | 50: '#FFF5F2',
17 | 100: '#FFF1EE',
18 | 200: '#FFE4DE',
19 | 300: '#FFD5CC',
20 | 400: '#FFBCAD',
21 | 500: '#FE795D',
22 | 600: '#EF562F',
23 | 700: '#EB4F27',
24 | 800: '#CC4522',
25 | 900: '#A5371B'
26 | }
27 | }
28 | }
29 | },
30 |
31 | plugins: [flowbitePlugin]
32 | } as Config;
33 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "moduleResolution": "bundler"
13 | }
14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
15 | // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
16 | //
17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18 | // from the referenced tsconfig.json - TypeScript does not merge them in
19 | }
20 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-3-sveltekit-ssr/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()]
6 | });
7 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from "astro/config";
3 |
4 | import tailwindcss from "@tailwindcss/vite";
5 |
6 | // https://astro.build/config
7 | export default defineConfig({
8 | vite: {
9 | plugins: [tailwindcss()],
10 | },
11 | output: "server",
12 | });
13 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3-4-astro-ssr",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev --port 3334",
7 | "build": "astro build",
8 | "preview": "astro preview --port 3334",
9 | "astro": "astro"
10 | },
11 | "dependencies": {
12 | "@tailwindcss/vite": "^4.0.7",
13 | "astro": "^5.3.0",
14 | "tailwindcss": "^4.0.7"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/pages/api/add-comment.ts:
--------------------------------------------------------------------------------
1 | import type { APIRoute } from "astro";
2 |
3 | export const POST: APIRoute = async ({ request }) => {
4 | try {
5 | const formData = await request.formData();
6 | console.log(formData);
7 | const body = formData.get("body");
8 | const url = new URL(request.url);
9 | const postId = url.searchParams.get("postId");
10 | const redirectHash = url.searchParams.get("redirectHash");
11 |
12 | if (!body || !postId) {
13 | return new Response("Missing required fields", { status: 400 });
14 | }
15 |
16 | const newComment = {
17 | body,
18 | email: "user@mailinator.com",
19 | name: "User",
20 | postId: Number(postId),
21 | };
22 |
23 | const response = await fetch("http://localhost:3300/comments", {
24 | method: "POST",
25 | headers: {
26 | "Content-Type": "application/json",
27 | },
28 | body: JSON.stringify(newComment),
29 | });
30 |
31 | if (!response.ok) throw new Error("Failed to create comment");
32 |
33 | const redirectUrl = new URL(
34 | `/posts/${postId}${redirectHash ? "#" + redirectHash : ""}`,
35 | url.origin
36 | );
37 | return Response.redirect(redirectUrl.toString(), 303);
38 | } catch (error) {
39 | console.error("Error creating comment:", error);
40 | return new Response("Error creating comment", { status: 500 });
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/pages/api/delete-comment.ts:
--------------------------------------------------------------------------------
1 | import type { APIRoute } from "astro";
2 |
3 | export const POST: APIRoute = async ({ request }) => {
4 | try {
5 | const formData = await request.formData();
6 | const commentId = formData.get("commentId");
7 | const postId = formData.get("postId");
8 | const url = new URL(request.url);
9 | const redirectHash = url.searchParams.get("redirectHash");
10 |
11 | if (!commentId || !postId) {
12 | return new Response("Missing required fields", { status: 400 });
13 | }
14 |
15 | const response = await fetch(
16 | `http://localhost:3300/comments/${commentId}`,
17 | {
18 | method: "DELETE",
19 | }
20 | );
21 |
22 | if (!response.ok) throw new Error("Failed to delete comment");
23 |
24 | const redirectUrl = new URL(
25 | `/posts/${postId}${redirectHash ? "#" + redirectHash : ""}`,
26 | url.origin
27 | );
28 | return Response.redirect(redirectUrl.toString(), 303);
29 | } catch (error) {
30 | console.error("Error deleting comment:", error);
31 | return new Response("Error deleting comment", { status: 500 });
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../layouts/Layout.astro";
3 | import type { IPost } from "../api-types";
4 |
5 | const fetchPosts = async (): Promise<{ posts: IPost[]; error: boolean }> => {
6 | try {
7 | const response = await fetch("http://localhost:3300/posts");
8 | const fetchedPosts = (await response.json()) as IPost[];
9 | return { posts: fetchedPosts, error: false };
10 | } catch (error) {
11 | console.error(error);
12 | return { posts: [], error: true };
13 | }
14 | };
15 |
16 | const { posts, error } = await fetchPosts();
17 | ---
18 |
19 |
20 |
21 |
Your Home Feed
22 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
--------------------------------------------------------------------------------
/apps/3-ssr/3-4-astro-ssr/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "include": [".astro/types.d.ts", "**/*"],
4 | "exclude": ["dist"]
5 | }
6 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/next.config.mjs:
--------------------------------------------------------------------------------
1 | import bundleAnalyzer from "@next/bundle-analyzer";
2 |
3 | const withBundleAnalyzer = bundleAnalyzer({
4 | enabled: process.env.ANALYZE === "true",
5 | });
6 |
7 | export default withBundleAnalyzer({
8 | reactStrictMode: false,
9 | eslint: {
10 | ignoreDuringBuilds: true,
11 | },
12 | experimental: {
13 | optimizePackageImports: ["@mantine/core", "@mantine/hooks"],
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4-1-nextjs-rsc",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev -p 3341",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mantine/core": "7.17.0",
13 | "@mantine/hooks": "7.17.0",
14 | "@tabler/icons-react": "3.17.0",
15 | "mantine-react-table": "^2.0.0-beta.6",
16 | "next": "15.1.7",
17 | "react": "19.0.0",
18 | "react-dom": "19.0.0"
19 | },
20 | "devDependencies": {
21 | "@next/bundle-analyzer": "^15.1.7",
22 | "@types/node": "^22",
23 | "@types/react": "^19.0.10",
24 | "@types/react-dom": "^19.0.4",
25 | "postcss": "^8.5.3",
26 | "postcss-preset-mantine": "1.17.0",
27 | "postcss-simple-vars": "^7.0.1",
28 | "typescript": "^5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | sending?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/4-streaming/4-1-nextjs-rsc/src/app/favicon.ico
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import React from "react";
3 | import { MantineProvider, ColorSchemeScript } from "@mantine/core";
4 | import { theme } from "../theme";
5 | import { AppLayout } from "@/components/AppLayout";
6 |
7 | export const metadata = {
8 | title: "Next JS RSC",
9 | };
10 |
11 | export default function RootLayout({ children }: { children: any }) {
12 | return (
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 | {children}
25 |
26 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { IPost } from "@/api-types";
2 | import { Card, Flex, Loader, Stack, Text, Title } from "@mantine/core";
3 | import Link from "next/link";
4 | import { Suspense } from "react";
5 |
6 | const fetchPosts = async () => {
7 | const fetchUrl = new URL(`http://localhost:3300/posts`);
8 | const response = await fetch(fetchUrl.href);
9 | const fetchedPosts = (await response.json()) as IPost[];
10 | return fetchedPosts;
11 | };
12 |
13 | export default async function HomePage() {
14 | const posts = await fetchPosts();
15 |
16 | return (
17 |
18 | Your Home Feed
19 |
20 | }>
21 | {posts.map((post) => (
22 |
27 |
36 | {post.title}
37 | {post.body}
38 |
39 | Go to post
40 |
41 |
42 |
43 | ))}
44 |
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/posts/[id]/actions.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { IComment } from "@/api-types";
4 | import { revalidatePath } from "next/cache";
5 |
6 | export const deleteComment = async (comment: IComment) => {
7 | const response = await fetch(`http://localhost:3300/comments/${comment.id}`, {
8 | method: "DELETE",
9 | });
10 |
11 | revalidatePath(`/posts/${comment.postId}`);
12 |
13 | return response.json() as Promise;
14 | };
15 |
16 | export const submitPostComment = async (formData: FormData) => {
17 | const comment = Object.fromEntries(formData.entries()) as unknown as IComment;
18 |
19 | const response = await fetch(`http://localhost:3300/comments`, {
20 | method: "POST",
21 | body: JSON.stringify(comment),
22 | headers: {
23 | "Content-type": "application/json; charset=UTF-8",
24 | },
25 | });
26 |
27 | revalidatePath(`/posts/${comment.postId}`);
28 |
29 | return response.json() as Promise;
30 | };
31 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/posts/[id]/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Alert } from "@mantine/core";
4 | import { IconAlertCircle } from "@tabler/icons-react";
5 |
6 | export default function Error() {
7 | return (
8 | } title="Bummer!" color="red">
9 | There was an error loading this post
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/posts/[id]/loading.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Loader, Stack } from "@mantine/core";
4 |
5 | export default function Loading() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/app/posts/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, Stack, Text, Title } from "@mantine/core";
2 | import { IComment, IPost, IUser } from "../../../api-types";
3 | import Link from "next/link";
4 | import CommentSection from "./CommentSection";
5 |
6 | const fetchPostAndComments = async (postId: number) => {
7 | const [post, comments] = await Promise.all([
8 | fetch(`http://localhost:3300/posts/${postId}`).then((res) => res.json()),
9 | fetch(`http://localhost:3300/posts/${postId}/comments`).then((res) =>
10 | res.json()
11 | ),
12 | ]);
13 | const user = await fetch(`http://localhost:3300/users/${post.userId}`).then(
14 | (res) => res.json()
15 | );
16 |
17 | return { post, user, comments } as {
18 | post: IPost;
19 | user: IUser;
20 | comments: IComment[];
21 | };
22 | };
23 |
24 | interface PostPageProps {
25 | params: { id: string };
26 | }
27 |
28 | // Server Component
29 | export default async function PostPage({ params }: PostPageProps) {
30 | const { id: postId } = params;
31 |
32 | const { post, user, comments } = await fetchPostAndComments(+postId);
33 |
34 | return (
35 |
36 |
37 | Post: {post?.id}
38 | {post?.title}
39 |
40 | By:{" "}
41 |
42 | {user?.name}
43 |
44 |
45 |
46 | {post.body}. {post.body}. {post.body}. {post.body}. {post.body}.
47 |
48 |
49 |
50 |
51 | Comments on this Post
52 |
53 |
54 | {/* Client Component */}
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
4 | import { useDisclosure } from "@mantine/hooks";
5 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
6 | import Link from "next/link";
7 |
8 | interface AppLayoutProps {
9 | children: React.ReactNode;
10 | }
11 |
12 | const links = [
13 | {
14 | icon: ,
15 | color: "blue",
16 | label: "Home Feed",
17 | href: "/",
18 | },
19 | {
20 | icon: ,
21 | color: "teal",
22 | label: "Users",
23 | href: "/users",
24 | },
25 | ];
26 |
27 | export function AppLayout({ children }: AppLayoutProps) {
28 | const [opened, { toggle }] = useDisclosure();
29 |
30 | return (
31 |
36 |
37 |
38 |
39 | Next JS RSC (App Router)
40 |
41 |
42 |
43 | {links.map((link) => (
44 |
45 | {link.label}
46 |
47 | ))}
48 |
49 | {children}
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/src/theme.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { createTheme } from "@mantine/core";
4 |
5 | export const theme = createTheme({
6 | /* Put your mantine theme override here */
7 | });
8 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-1-nextjs-rsc/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "plugins": [
20 | {
21 | "name": "next"
22 | }
23 | ],
24 | "paths": {
25 | "@/*": [
26 | "./src/*"
27 | ]
28 | },
29 | "target": "ES2017"
30 | },
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | ".next/types/**/*.ts"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/next.config.mjs:
--------------------------------------------------------------------------------
1 | import bundleAnalyzer from "@next/bundle-analyzer";
2 |
3 | const withBundleAnalyzer = bundleAnalyzer({
4 | enabled: process.env.ANALYZE === "true",
5 | });
6 |
7 | export default withBundleAnalyzer({
8 | reactStrictMode: false,
9 | eslint: {
10 | ignoreDuringBuilds: true,
11 | },
12 | experimental: {
13 | optimizePackageImports: ["@mantine/core", "@mantine/hooks"],
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4-2-nextjs-rsc-react-query",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev -p 3342",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mantine/core": "7.17.0",
13 | "@mantine/hooks": "7.17.0",
14 | "@tabler/icons-react": "3.17.0",
15 | "@tanstack/react-query": "^5.66.7",
16 | "mantine-react-table": "^2.0.0-beta.6",
17 | "next": "15.1.7",
18 | "react": "19.0.0",
19 | "react-dom": "19.0.0"
20 | },
21 | "devDependencies": {
22 | "@next/bundle-analyzer": "^15.1.7",
23 | "@tanstack/react-query-devtools": "^5.66.7",
24 | "@types/node": "^22",
25 | "@types/react": "^19.0.10",
26 | "@types/react-dom": "^19.0.4",
27 | "postcss": "^8.5.3",
28 | "postcss-preset-mantine": "1.17.0",
29 | "postcss-simple-vars": "^7.0.1",
30 | "typescript": "^5"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | sending?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/PostsFeed.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ActionIcon, Card, Flex, Stack, Text, Title } from "@mantine/core";
4 | import Link from "next/link";
5 | import { useSuspenseQuery } from "@tanstack/react-query";
6 | import { fetchPosts } from "./page";
7 | import { IconRefresh } from "@tabler/icons-react";
8 |
9 | export function PostsFeed() {
10 | const { data: posts, refetch: refetchPosts } = useSuspenseQuery({
11 | queryKey: ["posts"],
12 | queryFn: fetchPosts,
13 | // select: (data) =>
14 | // typeof window !== "undefined"
15 | // ? data.sort(() => Math.random() - 0.5)
16 | // : data,
17 | });
18 |
19 | return (
20 |
21 | refetchPosts()}>
22 |
23 |
24 |
25 | {posts.map((post) => (
26 |
31 |
40 | {post.title}
41 | {post.body}
42 |
43 | Go to post
44 |
45 |
46 |
47 | ))}
48 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/favicon.ico
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import React from "react";
3 | import { MantineProvider, ColorSchemeScript } from "@mantine/core";
4 | import { theme } from "../theme";
5 | import { AppLayout } from "@/components/AppLayout";
6 | import { ReactQueryProvider } from "./ReactQueryProvider";
7 |
8 | export const metadata = {
9 | title: "Next JS RSC",
10 | };
11 |
12 | export default function RootLayout({ children }: { children: any }) {
13 | return (
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 | {children}
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | dehydrate,
3 | HydrationBoundary,
4 | QueryClient,
5 | } from "@tanstack/react-query";
6 | import { Loader, Stack, Title } from "@mantine/core";
7 | import { IPost } from "@/api-types";
8 | import { PostsFeed } from "./PostsFeed";
9 | import { Suspense } from "react";
10 |
11 | export const fetchPosts = async () => {
12 | console.log("fetching posts");
13 | const fetchUrl = new URL(`http://localhost:3300/posts`);
14 | const response = await fetch(fetchUrl.href);
15 | const fetchedPosts = (await response.json()) as IPost[];
16 | return fetchedPosts;
17 | };
18 |
19 | export default async function HomePage() {
20 | const queryClient = new QueryClient();
21 |
22 | await queryClient.prefetchQuery({
23 | queryKey: ["posts"],
24 | queryFn: fetchPosts,
25 | });
26 |
27 | return (
28 |
29 |
30 | Your Home Feed
31 | }>
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/posts/[id]/actions.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { IComment } from "@/api-types";
4 | import { revalidatePath } from "next/cache";
5 |
6 | export const deleteComment = async (comment: IComment) => {
7 | const response = await fetch(`http://localhost:3300/comments/${comment.id}`, {
8 | method: "DELETE",
9 | });
10 |
11 | revalidatePath(`/posts/${comment.postId}`);
12 |
13 | return response.json() as Promise;
14 | };
15 |
16 | export const submitPostComment = async (formData: FormData) => {
17 | const comment = Object.fromEntries(formData.entries()) as unknown as IComment;
18 |
19 | const response = await fetch(`http://localhost:3300/comments`, {
20 | method: "POST",
21 | body: JSON.stringify(comment),
22 | headers: {
23 | "Content-type": "application/json; charset=UTF-8",
24 | },
25 | });
26 |
27 | revalidatePath(`/posts/${comment.postId}`);
28 |
29 | return response.json() as Promise;
30 | };
31 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/posts/[id]/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Alert } from "@mantine/core";
4 | import { IconAlertCircle } from "@tabler/icons-react";
5 |
6 | export default function Error() {
7 | return (
8 | } title="Bummer!" color="red">
9 | There was an error loading this post
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/posts/[id]/loading.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Loader, Stack } from "@mantine/core";
4 |
5 | export default function Loading() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/app/posts/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, Stack, Text, Title } from "@mantine/core";
2 | import { IComment, IPost, IUser } from "../../../api-types";
3 | import Link from "next/link";
4 | import CommentSection from "./CommentSection";
5 |
6 | const fetchPostAndComments = async (postId: number) => {
7 | const [post, comments] = await Promise.all([
8 | fetch(`http://localhost:3300/posts/${postId}`).then((res) => res.json()),
9 | fetch(`http://localhost:3300/posts/${postId}/comments`).then((res) =>
10 | res.json()
11 | ),
12 | ]);
13 | const user = await fetch(`http://localhost:3300/users/${post.userId}`).then(
14 | (res) => res.json()
15 | );
16 |
17 | return { post, user, comments } as {
18 | post: IPost;
19 | user: IUser;
20 | comments: IComment[];
21 | };
22 | };
23 |
24 | interface PostPageProps {
25 | params: { id: string };
26 | }
27 |
28 | // Server Component
29 | export default async function PostPage({ params }: PostPageProps) {
30 | const { id: postId } = params;
31 |
32 | const { post, user, comments } = await fetchPostAndComments(+postId);
33 |
34 | return (
35 |
36 |
37 | Post: {post?.id}
38 | {post?.title}
39 |
40 | By:{" "}
41 |
42 | {user?.name}
43 |
44 |
45 |
46 | {post.body}. {post.body}. {post.body}. {post.body}. {post.body}.
47 |
48 |
49 |
50 |
51 | Comments on this Post
52 |
53 |
54 | {/* Client Component */}
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
4 | import { useDisclosure } from "@mantine/hooks";
5 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
6 | import Link from "next/link";
7 |
8 | interface AppLayoutProps {
9 | children: React.ReactNode;
10 | }
11 |
12 | const links = [
13 | {
14 | icon: ,
15 | color: "blue",
16 | label: "Home Feed",
17 | href: "/",
18 | },
19 | {
20 | icon: ,
21 | color: "teal",
22 | label: "Users",
23 | href: "/users",
24 | },
25 | ];
26 |
27 | export function AppLayout({ children }: AppLayoutProps) {
28 | const [opened, { toggle }] = useDisclosure();
29 |
30 | return (
31 |
36 |
37 |
38 |
39 | Next JS RSC (App Router) React Query
40 |
41 |
42 |
43 | {links.map((link) => (
44 |
45 | {link.label}
46 |
47 | ))}
48 |
49 | {children}
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/src/theme.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { createTheme } from "@mantine/core";
4 |
5 | export const theme = createTheme({
6 | /* Put your mantine theme override here */
7 | });
8 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-2-nextjs-rsc-react-query/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/next.config.mjs:
--------------------------------------------------------------------------------
1 | import bundleAnalyzer from "@next/bundle-analyzer";
2 |
3 | const withBundleAnalyzer = bundleAnalyzer({
4 | enabled: process.env.ANALYZE === "true",
5 | });
6 |
7 | export default withBundleAnalyzer({
8 | reactStrictMode: false,
9 | eslint: {
10 | ignoreDuringBuilds: true,
11 | },
12 | experimental: {
13 | optimizePackageImports: ["@mantine/core", "@mantine/hooks"],
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4-3-nextjs-rsc-react-query-streaming",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev -p 3343",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mantine/core": "7.17.0",
13 | "@mantine/hooks": "7.17.0",
14 | "@tabler/icons-react": "3.17.0",
15 | "@tanstack/react-query": "^5.66.7",
16 | "@tanstack/react-query-next-experimental": "^5.66.7",
17 | "mantine-react-table": "^2.0.0-beta.6",
18 | "next": "15.1.7",
19 | "react": "19.0.0",
20 | "react-dom": "19.0.0"
21 | },
22 | "devDependencies": {
23 | "@next/bundle-analyzer": "^15.1.7",
24 | "@tanstack/react-query-devtools": "^5.66.7",
25 | "@types/node": "^22",
26 | "@types/react": "^19.0.10",
27 | "@types/react-dom": "^19.0.4",
28 | "postcss": "^8.5.3",
29 | "postcss-preset-mantine": "1.17.0",
30 | "postcss-simple-vars": "^7.0.1",
31 | "typescript": "^5"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | "postcss-preset-mantine": {},
4 | "postcss-simple-vars": {
5 | variables: {
6 | "mantine-breakpoint-xs": "36em",
7 | "mantine-breakpoint-sm": "48em",
8 | "mantine-breakpoint-md": "62em",
9 | "mantine-breakpoint-lg": "75em",
10 | "mantine-breakpoint-xl": "88em",
11 | },
12 | },
13 | },
14 | };
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | sending?: boolean;
39 | }
40 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/favicon.ico
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import "@mantine/core/styles.css";
2 | import React from "react";
3 | import { MantineProvider, ColorSchemeScript } from "@mantine/core";
4 | import { theme } from "../theme";
5 | import { AppLayout } from "@/components/AppLayout";
6 | import { ReactQueryProvider } from "./ReactQueryProvider";
7 |
8 | export const metadata = {
9 | title: "Next JS RSC",
10 | };
11 |
12 | export default function RootLayout({ children }: { children: any }) {
13 | return (
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 | {children}
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/posts/[id]/actions.tsx:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import { IComment } from "@/api-types";
4 | import { revalidatePath } from "next/cache";
5 |
6 | export const deleteComment = async (comment: IComment) => {
7 | const response = await fetch(`http://localhost:3300/comments/${comment.id}`, {
8 | method: "DELETE",
9 | });
10 |
11 | revalidatePath(`/posts/${comment.postId}`);
12 |
13 | return response.json() as Promise;
14 | };
15 |
16 | export const submitPostComment = async (formData: FormData) => {
17 | const comment = Object.fromEntries(formData.entries()) as unknown as IComment;
18 |
19 | const response = await fetch(`http://localhost:3300/comments`, {
20 | method: "POST",
21 | body: JSON.stringify(comment),
22 | headers: {
23 | "Content-type": "application/json; charset=UTF-8",
24 | },
25 | });
26 |
27 | revalidatePath(`/posts/${comment.postId}`);
28 |
29 | return response.json() as Promise;
30 | };
31 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/posts/[id]/error.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Alert } from "@mantine/core";
4 | import { IconAlertCircle } from "@tabler/icons-react";
5 |
6 | export default function Error() {
7 | return (
8 | } title="Bummer!" color="red">
9 | There was an error loading this post
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/posts/[id]/loading.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Loader, Stack } from "@mantine/core";
4 |
5 | export default function Loading() {
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/app/posts/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Flex, Stack, Text, Title } from "@mantine/core";
2 | import { IComment, IPost, IUser } from "../../../api-types";
3 | import Link from "next/link";
4 | import CommentSection from "./CommentSection";
5 |
6 | const fetchPostAndComments = async (postId: number) => {
7 | const [post, comments] = await Promise.all([
8 | fetch(`http://localhost:3300/posts/${postId}`).then((res) => res.json()),
9 | fetch(`http://localhost:3300/posts/${postId}/comments`).then((res) =>
10 | res.json()
11 | ),
12 | ]);
13 | const user = await fetch(`http://localhost:3300/users/${post.userId}`).then(
14 | (res) => res.json()
15 | );
16 |
17 | return { post, user, comments } as {
18 | post: IPost;
19 | user: IUser;
20 | comments: IComment[];
21 | };
22 | };
23 |
24 | interface PostPageProps {
25 | params: { id: string };
26 | }
27 |
28 | // Server Component
29 | export default async function PostPage({ params }: PostPageProps) {
30 | const { id: postId } = await params;
31 |
32 | const { post, user, comments } = await fetchPostAndComments(+postId);
33 |
34 | return (
35 |
36 |
37 | Post: {post?.id}
38 | {post?.title}
39 |
40 | By:{" "}
41 |
42 | {user?.name}
43 |
44 |
45 |
46 | {post.body}. {post.body}. {post.body}. {post.body}. {post.body}.
47 |
48 |
49 |
50 |
51 | Comments on this Post
52 |
53 |
54 | {/* Client Component */}
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/components/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Anchor, AppShell, Burger, Group } from "@mantine/core";
4 | import { useDisclosure } from "@mantine/hooks";
5 | import { IconHome, IconUsersGroup } from "@tabler/icons-react";
6 | import Link from "next/link";
7 |
8 | interface AppLayoutProps {
9 | children: React.ReactNode;
10 | }
11 |
12 | const links = [
13 | {
14 | icon: ,
15 | color: "blue",
16 | label: "Home Feed",
17 | href: "/",
18 | },
19 | {
20 | icon: ,
21 | color: "teal",
22 | label: "Users",
23 | href: "/users",
24 | },
25 | ];
26 |
27 | export function AppLayout({ children }: AppLayoutProps) {
28 | const [opened, { toggle }] = useDisclosure();
29 |
30 | return (
31 |
36 |
37 |
38 |
39 | Next JS RSC (App Router) React Query Streaming
40 |
41 |
42 |
43 | {links.map((link) => (
44 |
45 | {link.label}
46 |
47 | ))}
48 |
49 | {children}
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/src/theme.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { createTheme } from "@mantine/core";
4 |
5 | export const theme = createTheme({
6 | /* Put your mantine theme override here */
7 | });
8 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-3-nextjs-rsc-react-query-streaming/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": [
4 | "dom",
5 | "dom.iterable",
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "strict": true,
11 | "noEmit": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "bundler",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve",
18 | "incremental": true,
19 | "plugins": [
20 | {
21 | "name": "next"
22 | }
23 | ],
24 | "paths": {
25 | "@/*": [
26 | "./src/*"
27 | ]
28 | },
29 | "target": "ES2017"
30 | },
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx",
35 | ".next/types/**/*.ts"
36 | ],
37 | "exclude": [
38 | "node_modules"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist/
3 |
4 | # generated types
5 | .astro/
6 |
7 | # dependencies
8 | node_modules/
9 |
10 | # logs
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # environment variables
17 | .env
18 | .env.production
19 |
20 | # macOS-specific files
21 | .DS_Store
22 |
23 | # jetbrains setting folder
24 | .idea/
25 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["astro-build.astro-vscode"],
3 | "unwantedRecommendations": []
4 | }
5 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "command": "./node_modules/.bin/astro dev",
6 | "name": "Development server",
7 | "request": "launch",
8 | "type": "node-terminal"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { defineConfig } from "astro/config";
3 |
4 | import tailwindcss from "@tailwindcss/vite";
5 |
6 | import vercel from "@astrojs/vercel";
7 |
8 | // https://astro.build/config
9 | export default defineConfig({
10 | vite: {
11 | plugins: [tailwindcss()],
12 | },
13 |
14 | output: "server",
15 | adapter: vercel(),
16 | });
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "4-4-astro-server-islands",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "scripts": {
6 | "dev": "astro dev --port 3344",
7 | "build": "astro build",
8 | "preview": "astro preview --port 3344",
9 | "astro": "astro"
10 | },
11 | "dependencies": {
12 | "@astrojs/vercel": "^8.0.7",
13 | "@tailwindcss/vite": "^4.0.7",
14 | "astro": "^5.3.0",
15 | "tailwindcss": "^4.0.7"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/api-types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/components/HomeFeed.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import type { IPost } from "../api-types";
3 |
4 | const fetchPosts = async (): Promise<{ posts: IPost[]; error: boolean }> => {
5 | try {
6 | // Add artificial delay
7 | await new Promise((resolve) => setTimeout(resolve, 1000));
8 | const response = await fetch("http://localhost:3300/posts");
9 | const fetchedPosts = (await response.json()) as IPost[];
10 | return { posts: fetchedPosts, error: false };
11 | } catch (error) {
12 | console.error(error);
13 | return { posts: [], error: true };
14 | }
15 | };
16 |
17 | const { posts, error } = await fetchPosts();
18 | ---
19 |
20 |
21 |
Your Home Feed
22 |
35 |
36 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../layouts/Layout.astro";
3 | import HomeFeed from "../components/HomeFeed.astro";
4 | ---
5 |
6 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/pages/posts/[id].astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Layout from "../../layouts/Layout.astro";
3 | import Comments from "../../components/Comments.astro";
4 | import { type IPost, type IUser } from "../../api-types";
5 |
6 | export async function getStaticPaths() {
7 | await new Promise((resolve) => setTimeout(resolve, 3000));
8 | const response = await fetch(`http://localhost:3300/posts`);
9 | const posts = (await response.json()) as IPost[];
10 |
11 | return posts.map((post) => ({
12 | params: { id: post.id.toString() },
13 | }));
14 | }
15 |
16 | const { id: postId } = Astro.params;
17 |
18 | let post: IPost;
19 | let user: IUser;
20 |
21 | try {
22 | const [postResponse] = await Promise.all([
23 | fetch(`http://localhost:3300/posts/${postId}`),
24 | ]);
25 |
26 | post = (await postResponse.json()) as IPost;
27 |
28 | const userResponse = await fetch(
29 | `http://localhost:3300/users/${post.userId}`
30 | );
31 | user = (await userResponse.json()) as IUser;
32 | } catch (error) {
33 | console.error(error);
34 | return Astro.redirect("/error");
35 | }
36 | ---
37 |
38 |
39 |
40 |
41 |
Post: {post.id}
42 |
{post.title}
43 |
49 |
50 | {post.body}. {post.body}. {post.body}. {post.body}. {post.body}.
51 |
52 |
53 |
54 |
55 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
--------------------------------------------------------------------------------
/apps/4-streaming/4-4-astro-server-islands/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "astro/tsconfigs/strict",
3 | "include": [".astro/types.d.ts", "**/*"],
4 | "exclude": ["dist"]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-data-fetching/react-data-fetching",
3 | "private": true,
4 | "scripts": {
5 | "build": "turbo build",
6 | "dev": "turbo dev",
7 | "dev1-1": "turbo dev --filter 1-1-vite-componentdidmount",
8 | "dev1-2": "turbo dev --filter 1-2-vite-useeffect",
9 | "dev1-3": "turbo dev --filter 1-3-vite-reactquery",
10 | "dev1-4": "turbo dev --filter 1-4-vite-reactquery-refactor",
11 | "dev1-5": "turbo dev --filter 1-5-astro-react-spa",
12 | "dev2-1": "turbo dev --filter 2-1-nextjs-ssg",
13 | "dev2-2": "turbo dev --filter 2-2-astro-ssg",
14 | "dev2-3": "turbo dev --filter 2-3-astro-react-ssg",
15 | "dev3-1": "turbo dev --filter 3-1-nextjs-ssr",
16 | "dev3-2": "turbo dev --filter 3-2-remix-ssr",
17 | "dev3-3": "turbo dev --filter 3-3-sveltekit-ssr",
18 | "dev3-4": "turbo dev --filter 3-4-astro-ssr",
19 | "dev4-1": "turbo dev --filter 4-1-nextjs-rsc",
20 | "dev4-2": "turbo dev --filter 4-2-nextjs-rsc-react-query",
21 | "dev4-3": "turbo dev --filter 4-3-nextjs-rsc-react-query-streaming",
22 | "dev4-4": "turbo dev --filter 4-4-astro-server-islands",
23 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
24 | "lint": "turbo lint",
25 | "serve": "turbo serve --filter 0-json-server"
26 | },
27 | "devDependencies": {
28 | "prettier": "^3.5.1",
29 | "turbo": "2.4.2"
30 | },
31 | "engines": {
32 | "node": ">=18"
33 | },
34 | "packageManager": "pnpm@9.3.0"
35 | }
36 |
--------------------------------------------------------------------------------
/packages/types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@data-fetching/types",
3 | "type": "module",
4 | "version": "0.0.1",
5 | "types": "./types/types.d.ts",
6 | "main": "./types/types.d.ts",
7 | "scripts": {
8 | "dev": "tsc --watch",
9 | "build": "tsc"
10 | },
11 | "exports": {
12 | ".": {
13 | "types": "./types/types.d.ts"
14 | }
15 | },
16 | "devDependencies": {
17 | "typescript": "latest"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/types/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 |
25 | export interface IPost {
26 | userId: number;
27 | id: number;
28 | title: string;
29 | body: string;
30 | }
31 |
32 | export interface IComment {
33 | postId: number;
34 | id: number;
35 | name: string;
36 | email: string;
37 | body: string;
38 | }
39 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "src",
4 | "@types/node"
5 | ],
6 | "compilerOptions": {
7 | "declaration": true,
8 | "declarationDir": "types",
9 | "emitDeclarationOnly": true,
10 | "esModuleInterop": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "importHelpers": true,
13 | "jsx": "react-jsx",
14 | "lib": [
15 | "DOM",
16 | "DOM.Iterable",
17 | "ESNext"
18 | ],
19 | "module": "esnext",
20 | "moduleResolution": "node",
21 | "noFallthroughCasesInSwitch": true,
22 | "noImplicitReturns": true,
23 | "noUnusedLocals": true,
24 | "noUnusedParameters": true,
25 | "skipLibCheck": true,
26 | "sourceMap": true,
27 | "strict": true,
28 | "target": "ES6",
29 | },
30 | "references": [
31 | {
32 | "path": "./tsconfig.node.json"
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/packages/types/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/types/types/types.d.ts:
--------------------------------------------------------------------------------
1 | export interface IUser {
2 | id: number;
3 | name: string;
4 | username: string;
5 | email: string;
6 | address: {
7 | street: string;
8 | suite: string;
9 | city: string;
10 | zipcode: string;
11 | geo: {
12 | lat: string;
13 | lng: string;
14 | };
15 | };
16 | phone: string;
17 | website: string;
18 | company: {
19 | name: string;
20 | catchPhrase: string;
21 | bs: string;
22 | };
23 | }
24 | export interface IPost {
25 | userId: number;
26 | id: number;
27 | title: string;
28 | body: string;
29 | }
30 | export interface IComment {
31 | postId: number;
32 | id: number;
33 | name: string;
34 | email: string;
35 | body: string;
36 | }
37 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/**"
3 | - "packages/*"
--------------------------------------------------------------------------------
/react-data-fetching-compressed.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/react-data-fetching-compressed.pdf
--------------------------------------------------------------------------------
/reactquery.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KevinVandy/react-data-fetching/d880a77bfbb433e7f84254a63f00a121a2a8947a/reactquery.pdf
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "globalDependencies": ["**/.env.*local"],
4 | "tasks": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "outputs": [".next/**", "!.next/cache/**"]
8 | },
9 | "lint": {
10 | "dependsOn": ["^lint"]
11 | },
12 | "dev": {
13 | "cache": false,
14 | "persistent": true
15 | },
16 | "serve": {
17 | "cache": false,
18 | "persistent": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------