eny.space Landingpage
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(components): add layout, list, and user detail components with routing and sample data

+544
+32
components/Layout.tsx
··· 1 + import React, { ReactNode } from "react"; 2 + import Link from "next/link"; 3 + import Head from "next/head"; 4 + 5 + type Props = { 6 + children?: ReactNode; 7 + title?: string; 8 + }; 9 + 10 + const Layout = ({ children, title = "This is the default title" }: Props) => ( 11 + <div> 12 + <Head> 13 + <title>{title}</title> 14 + <meta charSet="utf-8" /> 15 + <meta name="viewport" content="initial-scale=1.0, width=device-width" /> 16 + </Head> 17 + <header> 18 + <nav> 19 + <Link href="/">Home</Link> | <Link href="/about">About</Link> |{" "} 20 + <Link href="/users">Users List</Link> |{" "} 21 + <a href="/api/users">Users API</a> 22 + </nav> 23 + </header> 24 + {children} 25 + <footer> 26 + <hr /> 27 + <span>I'm here to stay (Footer)</span> 28 + </footer> 29 + </div> 30 + ); 31 + 32 + export default Layout;
+19
components/List.tsx
··· 1 + import * as React from "react"; 2 + import ListItem from "./ListItem"; 3 + import { User } from "../interfaces"; 4 + 5 + type Props = { 6 + items: User[]; 7 + }; 8 + 9 + const List = ({ items }: Props) => ( 10 + <ul> 11 + {items.map((item) => ( 12 + <li key={item.id}> 13 + <ListItem data={item} /> 14 + </li> 15 + ))} 16 + </ul> 17 + ); 18 + 19 + export default List;
+16
components/ListDetail.tsx
··· 1 + import * as React from "react"; 2 + 3 + import { User } from "../interfaces"; 4 + 5 + type ListDetailProps = { 6 + item: User; 7 + }; 8 + 9 + const ListDetail = ({ item: user }: ListDetailProps) => ( 10 + <div> 11 + <h1>Detail for {user.name}</h1> 12 + <p>ID: {user.id}</p> 13 + </div> 14 + ); 15 + 16 + export default ListDetail;
+16
components/ListItem.tsx
··· 1 + import React from "react"; 2 + import Link from "next/link"; 3 + 4 + import { User } from "../interfaces"; 5 + 6 + type Props = { 7 + data: User; 8 + }; 9 + 10 + const ListItem = ({ data }: Props) => ( 11 + <Link href="/users/[id]" as={`/users/${data.id}`}> 12 + {data.id}:{data.name} 13 + </Link> 14 + ); 15 + 16 + export default ListItem;
+10
interfaces/index.ts
··· 1 + // You can include shared interfaces/types in a separate file 2 + // and then use them in any component by importing them. For 3 + // example, to import the interface below do: 4 + // 5 + // import { User } from 'path/to/interfaces'; 6 + 7 + export type User = { 8 + id: number; 9 + name: string; 10 + };
+14
pages/about.tsx
··· 1 + import Link from "next/link"; 2 + import Layout from "../components/Layout"; 3 + 4 + const AboutPage = () => ( 5 + <Layout title="About | Next.js + TypeScript Example"> 6 + <h1>About</h1> 7 + <p>This is the about page</p> 8 + <p> 9 + <Link href="/">Go home</Link> 10 + </p> 11 + </Layout> 12 + ); 13 + 14 + export default AboutPage;
+16
pages/api/users/index.ts
··· 1 + import { NextApiRequest, NextApiResponse } from "next"; 2 + import { sampleUserData } from "@/utils/sample-data"; 3 + 4 + const handler = (_req: NextApiRequest, res: NextApiResponse) => { 5 + try { 6 + if (!Array.isArray(sampleUserData)) { 7 + throw new Error("Cannot find user data"); 8 + } 9 + 10 + res.status(200).json(sampleUserData); 11 + } catch (err: any) { 12 + res.status(500).json({ statusCode: 500, message: err.message }); 13 + } 14 + }; 15 + 16 + export default handler;
+13
pages/index.tsx
··· 1 + import Link from "next/link"; 2 + import Layout from "../components/Layout"; 3 + 4 + const IndexPage = () => ( 5 + <Layout title="Home | Next.js + TypeScript Example"> 6 + <h1>Hello Next.js 👋</h1> 7 + <p> 8 + <Link href="/about">About</Link> 9 + </p> 10 + </Layout> 11 + ); 12 + 13 + export default IndexPage;
+61
pages/users/[id].tsx
··· 1 + import { GetStaticProps, GetStaticPaths } from "next"; 2 + 3 + import { User } from "../../interfaces"; 4 + import { sampleUserData } from "../../utils/sample-data"; 5 + import Layout from "../../components/Layout"; 6 + import ListDetail from "../../components/ListDetail"; 7 + 8 + type Props = { 9 + item?: User; 10 + errors?: string; 11 + }; 12 + 13 + const StaticPropsDetail = ({ item, errors }: Props) => { 14 + if (errors) { 15 + return ( 16 + <Layout title="Error | Next.js + TypeScript Example"> 17 + <p> 18 + <span style={{ color: "red" }}>Error:</span> {errors} 19 + </p> 20 + </Layout> 21 + ); 22 + } 23 + 24 + return ( 25 + <Layout 26 + title={`${ 27 + item ? item.name : "User Detail" 28 + } | Next.js + TypeScript Example`} 29 + > 30 + {item && <ListDetail item={item} />} 31 + </Layout> 32 + ); 33 + }; 34 + 35 + export default StaticPropsDetail; 36 + 37 + export const getStaticPaths: GetStaticPaths = async () => { 38 + // Get the paths we want to pre-render based on users 39 + const paths = sampleUserData.map((user) => ({ 40 + params: { id: user.id.toString() }, 41 + })); 42 + 43 + // We'll pre-render only these paths at build time. 44 + // { fallback: false } means other routes should 404. 45 + return { paths, fallback: false }; 46 + }; 47 + 48 + // This function gets called at build time on server-side. 49 + // It won't be called on client-side, so you can even do 50 + // direct database queries. 51 + export const getStaticProps: GetStaticProps = async ({ params }) => { 52 + try { 53 + const id = params?.id; 54 + const item = sampleUserData.find((data) => data.id === Number(id)); 55 + // By returning { props: item }, the StaticPropsDetail component 56 + // will receive `item` as a prop at build time 57 + return { props: { item } }; 58 + } catch (err: any) { 59 + return { props: { errors: err.message } }; 60 + } 61 + };
+35
pages/users/index.tsx
··· 1 + import { GetStaticProps } from "next"; 2 + import Link from "next/link"; 3 + 4 + import { User } from "../../interfaces"; 5 + import { sampleUserData } from "../../utils/sample-data"; 6 + import Layout from "../../components/Layout"; 7 + import List from "../../components/List"; 8 + 9 + type Props = { 10 + items: User[]; 11 + }; 12 + 13 + const WithStaticProps = ({ items }: Props) => ( 14 + <Layout title="Users List | Next.js + TypeScript Example"> 15 + <h1>Users List</h1> 16 + <p> 17 + Example fetching data from inside <code>getStaticProps()</code>. 18 + </p> 19 + <p>You are currently on: /users</p> 20 + <List items={items} /> 21 + <p> 22 + <Link href="/">Go home</Link> 23 + </p> 24 + </Layout> 25 + ); 26 + 27 + export const getStaticProps: GetStaticProps = async () => { 28 + // Example for including static props in a Next.js function component page. 29 + // Don't forget to include the respective types for any props passed into 30 + // the component. 31 + const items: User[] = sampleUserData; 32 + return { props: { items } }; 33 + }; 34 + 35 + export default WithStaticProps;
+145
types/cache-life.d.ts
··· 1 + // Type definitions for Next.js cacheLife configs 2 + 3 + declare module 'next/cache' { 4 + export { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache' 5 + export { 6 + updateTag, 7 + revalidateTag, 8 + revalidatePath, 9 + refresh, 10 + } from 'next/dist/server/web/spec-extension/revalidate' 11 + export { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store' 12 + 13 + 14 + /** 15 + * Cache this `"use cache"` for a timespan defined by the `"default"` profile. 16 + * ``` 17 + * stale: 300 seconds (5 minutes) 18 + * revalidate: 900 seconds (15 minutes) 19 + * expire: never 20 + * ``` 21 + * 22 + * This cache may be stale on clients for 5 minutes before checking with the server. 23 + * If the server receives a new request after 15 minutes, start revalidating new values in the background. 24 + * It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request. 25 + */ 26 + export function cacheLife(profile: "default"): void 27 + 28 + /** 29 + * Cache this `"use cache"` for a timespan defined by the `"seconds"` profile. 30 + * ``` 31 + * stale: 30 seconds 32 + * revalidate: 1 seconds 33 + * expire: 60 seconds (1 minute) 34 + * ``` 35 + * 36 + * This cache may be stale on clients for 30 seconds before checking with the server. 37 + * If the server receives a new request after 1 seconds, start revalidating new values in the background. 38 + * If this entry has no traffic for 1 minute it will expire. The next request will recompute it. 39 + */ 40 + export function cacheLife(profile: "seconds"): void 41 + 42 + /** 43 + * Cache this `"use cache"` for a timespan defined by the `"minutes"` profile. 44 + * ``` 45 + * stale: 300 seconds (5 minutes) 46 + * revalidate: 60 seconds (1 minute) 47 + * expire: 3600 seconds (1 hour) 48 + * ``` 49 + * 50 + * This cache may be stale on clients for 5 minutes before checking with the server. 51 + * If the server receives a new request after 1 minute, start revalidating new values in the background. 52 + * If this entry has no traffic for 1 hour it will expire. The next request will recompute it. 53 + */ 54 + export function cacheLife(profile: "minutes"): void 55 + 56 + /** 57 + * Cache this `"use cache"` for a timespan defined by the `"hours"` profile. 58 + * ``` 59 + * stale: 300 seconds (5 minutes) 60 + * revalidate: 3600 seconds (1 hour) 61 + * expire: 86400 seconds (1 day) 62 + * ``` 63 + * 64 + * This cache may be stale on clients for 5 minutes before checking with the server. 65 + * If the server receives a new request after 1 hour, start revalidating new values in the background. 66 + * If this entry has no traffic for 1 day it will expire. The next request will recompute it. 67 + */ 68 + export function cacheLife(profile: "hours"): void 69 + 70 + /** 71 + * Cache this `"use cache"` for a timespan defined by the `"days"` profile. 72 + * ``` 73 + * stale: 300 seconds (5 minutes) 74 + * revalidate: 86400 seconds (1 day) 75 + * expire: 604800 seconds (1 week) 76 + * ``` 77 + * 78 + * This cache may be stale on clients for 5 minutes before checking with the server. 79 + * If the server receives a new request after 1 day, start revalidating new values in the background. 80 + * If this entry has no traffic for 1 week it will expire. The next request will recompute it. 81 + */ 82 + export function cacheLife(profile: "days"): void 83 + 84 + /** 85 + * Cache this `"use cache"` for a timespan defined by the `"weeks"` profile. 86 + * ``` 87 + * stale: 300 seconds (5 minutes) 88 + * revalidate: 604800 seconds (1 week) 89 + * expire: 2592000 seconds (1 month) 90 + * ``` 91 + * 92 + * This cache may be stale on clients for 5 minutes before checking with the server. 93 + * If the server receives a new request after 1 week, start revalidating new values in the background. 94 + * If this entry has no traffic for 1 month it will expire. The next request will recompute it. 95 + */ 96 + export function cacheLife(profile: "weeks"): void 97 + 98 + /** 99 + * Cache this `"use cache"` for a timespan defined by the `"max"` profile. 100 + * ``` 101 + * stale: 300 seconds (5 minutes) 102 + * revalidate: 2592000 seconds (1 month) 103 + * expire: 31536000 seconds (365 days) 104 + * ``` 105 + * 106 + * This cache may be stale on clients for 5 minutes before checking with the server. 107 + * If the server receives a new request after 1 month, start revalidating new values in the background. 108 + * If this entry has no traffic for 365 days it will expire. The next request will recompute it. 109 + */ 110 + export function cacheLife(profile: "max"): void 111 + 112 + /** 113 + * Cache this `"use cache"` using a custom timespan. 114 + * ``` 115 + * stale: ... // seconds 116 + * revalidate: ... // seconds 117 + * expire: ... // seconds 118 + * ``` 119 + * 120 + * This is similar to Cache-Control: max-age=`stale`,s-max-age=`revalidate`,stale-while-revalidate=`expire-revalidate` 121 + * 122 + * If a value is left out, the lowest of other cacheLife() calls or the default, is used instead. 123 + */ 124 + export function cacheLife(profile: { 125 + /** 126 + * This cache may be stale on clients for ... seconds before checking with the server. 127 + */ 128 + stale?: number, 129 + /** 130 + * If the server receives a new request after ... seconds, start revalidating new values in the background. 131 + */ 132 + revalidate?: number, 133 + /** 134 + * If this entry has no traffic for ... seconds it will expire. The next request will recompute it. 135 + */ 136 + expire?: number 137 + }): void 138 + 139 + 140 + import { cacheTag } from 'next/dist/server/use-cache/cache-tag' 141 + export { cacheTag } 142 + 143 + export const unstable_cacheTag: typeof cacheTag 144 + export const unstable_cacheLife: typeof cacheLife 145 + }
+59
types/routes.d.ts
··· 1 + // This file is generated automatically by Next.js 2 + // Do not edit this file manually 3 + 4 + type AppRoutes = never 5 + type PageRoutes = "/" | "/about" | "/users" | "/users/[id]" 6 + type LayoutRoutes = never 7 + type RedirectRoutes = never 8 + type RewriteRoutes = never 9 + type Routes = AppRoutes | PageRoutes | LayoutRoutes | RedirectRoutes | RewriteRoutes 10 + 11 + 12 + interface ParamMap { 13 + "/": {} 14 + "/about": {} 15 + "/users": {} 16 + "/users/[id]": { "id": string; } 17 + } 18 + 19 + 20 + export type ParamsOf<Route extends Routes> = ParamMap[Route] 21 + 22 + interface LayoutSlotMap { 23 + } 24 + 25 + 26 + export type { AppRoutes, PageRoutes, LayoutRoutes, RedirectRoutes, RewriteRoutes, ParamMap } 27 + 28 + declare global { 29 + /** 30 + * Props for Next.js App Router page components 31 + * @example 32 + * ```tsx 33 + * export default function Page(props: PageProps<'/blog/[slug]'>) { 34 + * const { slug } = await props.params 35 + * return <div>Blog post: {slug}</div> 36 + * } 37 + * ``` 38 + */ 39 + interface PageProps<AppRoute extends AppRoutes> { 40 + params: Promise<ParamMap[AppRoute]> 41 + searchParams: Promise<Record<string, string | string[] | undefined>> 42 + } 43 + 44 + /** 45 + * Props for Next.js App Router layout components 46 + * @example 47 + * ```tsx 48 + * export default function Layout(props: LayoutProps<'/dashboard'>) { 49 + * return <div>{props.children}</div> 50 + * } 51 + * ``` 52 + */ 53 + type LayoutProps<LayoutRoute extends LayoutRoutes> = { 54 + params: Promise<ParamMap[LayoutRoute]> 55 + children: React.ReactNode 56 + } & { 57 + [K in LayoutSlotMap[LayoutRoute]]: React.ReactNode 58 + } 59 + }
+88
types/validator.ts
··· 1 + // This file is generated automatically by Next.js 2 + // Do not edit this file manually 3 + // This file validates that all pages and layouts export the correct types 4 + 5 + 6 + import type { NextApiHandler } from "next/types.js" 7 + 8 + type PagesPageConfig = { 9 + default: React.ComponentType<any> | ((props: any) => React.ReactNode | Promise<React.ReactNode> | never | void) 10 + getStaticProps?: (context: any) => Promise<any> | any 11 + getStaticPaths?: (context: any) => Promise<any> | any 12 + getServerSideProps?: (context: any) => Promise<any> | any 13 + getInitialProps?: (context: any) => Promise<any> | any 14 + /** 15 + * Segment configuration for legacy Pages Router pages. 16 + * Validated at build-time by parsePagesSegmentConfig. 17 + */ 18 + config?: { 19 + maxDuration?: number 20 + runtime?: 'edge' | 'experimental-edge' | 'nodejs' | string // necessary unless config is exported as const 21 + regions?: string[] 22 + } 23 + } 24 + 25 + type ApiRouteConfig = { 26 + default: (req: any, res: any) => ReturnType<NextApiHandler> 27 + config?: { 28 + api?: { 29 + bodyParser?: boolean | { sizeLimit?: string } 30 + responseLimit?: string | number | boolean 31 + externalResolver?: boolean 32 + } 33 + runtime?: 'edge' | 'experimental-edge' | 'nodejs' | string // necessary unless config is exported as const 34 + maxDuration?: number 35 + } 36 + } 37 + 38 + 39 + 40 + 41 + 42 + 43 + // Validate ../../pages/about.tsx 44 + { 45 + type __IsExpected<Specific extends PagesPageConfig> = Specific 46 + const handler = {} as typeof import("../../pages/about.js") 47 + type __Check = __IsExpected<typeof handler> 48 + // @ts-ignore 49 + type __Unused = __Check 50 + } 51 + 52 + // Validate ../../pages/index.tsx 53 + { 54 + type __IsExpected<Specific extends PagesPageConfig> = Specific 55 + const handler = {} as typeof import("../../pages/index.js") 56 + type __Check = __IsExpected<typeof handler> 57 + // @ts-ignore 58 + type __Unused = __Check 59 + } 60 + 61 + // Validate ../../pages/users/[id].tsx 62 + { 63 + type __IsExpected<Specific extends PagesPageConfig> = Specific 64 + const handler = {} as typeof import("../../pages/users/[id].js") 65 + type __Check = __IsExpected<typeof handler> 66 + // @ts-ignore 67 + type __Unused = __Check 68 + } 69 + 70 + // Validate ../../pages/users/index.tsx 71 + { 72 + type __IsExpected<Specific extends PagesPageConfig> = Specific 73 + const handler = {} as typeof import("../../pages/users/index.js") 74 + type __Check = __IsExpected<typeof handler> 75 + // @ts-ignore 76 + type __Unused = __Check 77 + } 78 + 79 + // Validate ../../pages/api/users/index.ts 80 + { 81 + type __IsExpected<Specific extends ApiRouteConfig> = Specific 82 + const handler = {} as typeof import("../../pages/api/users/index.js") 83 + type __Check = __IsExpected<typeof handler> 84 + // @ts-ignore 85 + type __Unused = __Check 86 + } 87 + 88 +
+11
utils/get-stripejs.ts
··· 1 + import { Stripe, loadStripe } from "@stripe/stripe-js"; 2 + 3 + let stripePromise: Promise<Stripe | null>; 4 + const getStripe = () => { 5 + if (!stripePromise) { 6 + stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!); 7 + } 8 + return stripePromise; 9 + }; 10 + 11 + export default getStripe;
+9
utils/sample-data.ts
··· 1 + import { User } from "../interfaces"; 2 + 3 + /** Dummy user data. */ 4 + export const sampleUserData: User[] = [ 5 + { id: 101, name: "Alice" }, 6 + { id: 102, name: "Bob" }, 7 + { id: 103, name: "Caroline" }, 8 + { id: 104, name: "Dave" }, 9 + ];