npx create-next-app@latest nextjs-dashboard --use-pnpm --example "https://github.com/vercel/next-learn/tree/main/dashboard/starter-example"
pnpm create next-app <app-name>
--ts | --js # using ts | js
--use-npm | --use-pnpm | --use-yarn | --use-bun # Explicitly tell the CLI to bootstrap the app using specific package manger
'use client' // This is a Client Component, which means you can use event listeners and hooks.
...
const pathname = usePathname();
...
<Link
key={link.name}
href={link.href}
className={clsx(
'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
{
'bg-sky-100 text-blue-600': pathname === link.href,
},
)}
>
<LinkIcon className="w-6" />
<p className="hidden md:block">{link.name}</p>
</Link>
Search process:
Capture the user’s input.
Update the URL with the search params.
Keep the URL in sync with the input field.
Update the table to reflect the search query.
useSearchParams() hook vs. the searchParams props
useSearchParams(): use in Client ComponentsearchParams: use in Server ComponentuseDebouncedCallback from use-debounce to delay running function after specific time
Update process:
Create a new dynamic route segment with the invoice id.
Read the invoice id from the page params.
Fetch the specific invoice from your database.
Pre-populate the form with the invoice data.
Update the invoice data in your database.
export default async function Page({ params }: { params: { id: string } }) {
const id = params.id;
// ...
}
Dynamic route segments can created by wrapping a folder’s name in square brackets. For example, [id], [post] or [slug]
In addition to searchParams, page components also accept a prop called params which you can use to access the id
Next will expose error if using domain different with its default localhost:3000 in dev environmnet. Add to next.config.js this config:
const nextConfig = {
experimental: {
serverActions: {
// allowedForwardedHosts: ['localhost:3000', '8g8q0rph-3000.asse.devtunnels.ms'],
allowedOrigins: [
'localhost:3000',
'8g8q0rph-3000.asse.devtunnels.ms',
],
},
},
};
Next.js serves as a catch-all for unexpected errors in your route segments in error.tsx file
Use notFound() from next/navigation to throw (navigate) to not-found.tsx page
Form validate:
useFormState() hook in form
use zod to validate at server
Display error in client using aria-*:
aria-describedby at input fields.
aria-live, aria-atomic at message.
Example:
<select
...
aria-describedby="customer-error"
>
</select>
...
<div id="customer-error" aria-live="polite" aria-atomic="true">
{state.errors?.customerId &&
state.errors.customerId.map((error: string) => (
<p className="mt-2 text-sm text-red-500" key={error}>
{error}
</p>
))}
</div>