├── .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 | 2 | 3 | 9 | 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 |
17 |

© {new Date().getFullYear()} Astro React SPA

18 |
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 | 2 | 3 | 9 | 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 |
23 | { 24 | posts.map((post) => ( 25 | 26 |
27 |

{post.title}

28 |

{post.body}

29 |

Go to post

30 |
31 |
32 | )) 33 | } 34 |
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 | 2 | 3 | 9 | 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 |
12 | {#if error} 13 | 14 | Bummer! 15 | There was an error fetching posts 16 | 17 | {:else} 18 | {#each posts as post (post.id)} 19 | 20 | 21 |

{post.title}

22 |

{post.body}

23 |

Go to post

24 |
25 |
26 | {/each} 27 | {/if} 28 |
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 | 2 | 3 | 9 | 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 |
23 | { 24 | posts.map((post) => ( 25 | 26 |
27 |

{post.title}

28 |

{post.body}

29 |

Go to post

30 |
31 |
32 | )) 33 | } 34 |
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 | <Link href={`/users/${user?.id}`} style={{ textDecoration: "none" }}> 42 | {user?.name} 43 | </Link> 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 | <Link href={`/users/${user?.id}`} style={{ textDecoration: "none" }}> 42 | {user?.name} 43 | </Link> 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 | <Link href={`/users/${user?.id}`} style={{ textDecoration: "none" }}> 42 | {user?.name} 43 | </Link> 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 | 2 | 3 | 9 | 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 |
23 | { 24 | posts.map((post) => ( 25 | 26 |
27 |

{post.title}

28 |

{post.body}

29 |

Go to post

30 |
31 |
32 | )) 33 | } 34 |
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 |
9 |
10 |
11 |
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 |

44 | By: {user.name} 48 |

49 |

50 | {post.body}. {post.body}. {post.body}. {post.body}. {post.body}. 51 |

52 |
53 | 54 | 55 |
56 |
57 |
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 | --------------------------------------------------------------------------------