Skip to content

Commit

Permalink
Merge branch 'canary' into fix/unstable-cache-draft-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk authored Jul 15, 2024
2 parents eea2a95 + b7f9f1f commit dadc291
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default async function Cart() {
export default function Navigation() {
return (
<>
<Suspense loading={<LoadingIcon />}>
<Suspense fallback={<LoadingIcon />}>
<Cart />
</Suspense>
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,10 @@ To maintain a predictable order, we recommend the following:
- Extract shared styles into a separate shared component.
- If using [Tailwind](/docs/app/building-your-application/styling/tailwind-css), import the stylesheet at the top of the file, preferably in the [Root Layout](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required).

> **Good to know:** CSS ordering behaves differently in development mode, always ensure to check preview deployments to verify the final CSS order in your production build.
> **Good to know:**
>
> - CSS ordering can behave differently in development mode, always ensure to check the build (`next build`) to verify the final CSS order.
> - You can use the [`cssChunking`](/docs/app/api-reference/next-config-js/cssChunking) option in `next.config.js` to control how CSS is chunked.
</AppOnly>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ To prevent unnecessary calls to your authentication provider's API or database,
```ts filename="app/actions/auth.ts" switcher
import { SignupFormSchema, FormState } from '@/app/lib/definitions'

export async function signup(state: FormState, formData) {
export async function signup(state: FormState, formData: FormData) {
// Validate form fields
const validatedFields = SignupFormSchema.safeParse({
name: formData.get('name'),
Expand Down Expand Up @@ -254,7 +254,7 @@ import { useActionState } from 'react'
import { signup } from '@/app/actions/auth'

export function SignupForm() {
const [state, action] = useActionState(signup, undefined)
const [state, action, pending] = useActionState(signup, undefined)

return (
<form action={action}>
Expand Down
40 changes: 40 additions & 0 deletions docs/02-app/02-api-reference/05-next-config-js/cssChunking.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: cssChunking (Experimental)
description: Use the `cssChunking` option to control how CSS files are chunked in your Next.js application.
---

CSS Chunking is a strategy used to improve the performance of your web application by splitting and re-ordering CSS files into chunks. This allows you to load only the CSS that is needed for a specific route, instead of loading all the application's CSS at once.

You can control how CSS files are chunked using the `experimental.cssChunking` option in your `next.config.js` file:

```tsx filename="next.config.ts" switcher
import type { NextConfig } from 'next'

const nextConfig = {
experimental: {
cssChunking: 'loose', // default
},
} satisfies NextConfig

export default nextConfig
```

```js filename="next.config.js" switcher
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
cssChunking: 'loose', // default
},
}

module.exports = nextConfig
```

## Options

- **`'loose'` (default)**: Next.js will try to merge CSS files whenever possible, determining explicit and implicit dependencies between files from import order to reduce the number of chunks and therefore the number of requests.
- **`'strict'`**: Next.js will load CSS files in the correct order they are imported into your files, which can lead to more chunks and requests.

You may consider using `'strict'` if you run into unexpected CSS behavior. For example, if you import `a.css` and `b.css` in different files using a different `import` order (`a` before `b`, or `b` before `a`), `'loose'` will merge the files in any other and assume there are no dependencies between them. However, if `b.css` depends on `a.css`, you may want to use `'strict'` to prevent the files from being merged, and instead, load them in the order they are imported - which can result in more chunks and requests.

For most applications, we recommend `'loose'` as it leads to fewer requests and better performance.
2 changes: 1 addition & 1 deletion examples/active-class-name/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# activeClassName example

ReactRouter has a convenience property on the `Link` element to allow an author to set the _active_ className on a link. This example replicates that functionality using Next's own `Link`.
ReactRouter has a convenience property on the `Link` element to allow an author to set the _active_ className on a link. This example replicates that functionality using Next's own `Link` component with the new app router.

## Deploy your own

Expand Down
17 changes: 17 additions & 0 deletions examples/active-class-name/app/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";

import { usePathname } from "next/navigation";
import Nav from "../../components/Nav";

const SlugPage = () => {
const pathname = usePathname();

return (
<>
<Nav />
<p>Hello, I'm the {pathname} page</p>
</>
);
};

export default SlugPage;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Nav from "../components/Nav";
import Nav from "../../components/Nav";

const AboutPage = () => (
<>
Expand Down
16 changes: 16 additions & 0 deletions examples/active-class-name/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const metadata = {
title: "Next.js",
description: "Generated by Next.js",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Nav from "../components/Nav";
import Nav from "../../components/Nav";

const News = () => (
<>
Expand Down
File renamed without changes.
40 changes: 11 additions & 29 deletions examples/active-class-name/components/ActiveLink.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
import { useRouter } from "next/router";
"use client";

import Link, { LinkProps } from "next/link";
import React, { PropsWithChildren, useEffect, useState } from "react";
import { NextRouter } from "next/src/shared/lib/router/router";
import { resolveHref } from "next/dist/client/resolve-href";
import { usePathname } from "next/navigation";

const getLinkUrl = (params: {
router: NextRouter;
href: LinkProps["href"];
as: LinkProps["as"];
}): string => {
const getLinkUrl = (href: LinkProps["href"], as?: LinkProps["as"]): string => {
// Dynamic route will be matched via props.as
// Static route will be matched via props.href
if (params.as) return resolveHref(params.router, params.as);

const [resolvedHref, resolvedAs] = resolveHref(
params.router,
params.href,
true,
);

return resolvedAs || resolvedHref;
if (as) return as.toString();
return href.toString();
};

type ActiveLinkProps = LinkProps & {
Expand All @@ -33,22 +22,15 @@ const ActiveLink = ({
className,
...props
}: PropsWithChildren<ActiveLinkProps>) => {
const router = useRouter();
const pathname = usePathname();
const [computedClassName, setComputedClassName] = useState(className);

useEffect(() => {
// Check if the router fields are updated client-side
if (router.isReady) {
const linkUrl = getLinkUrl({
router,
href: props.href,
as: props.as,
});
if (pathname) {
const linkUrl = getLinkUrl(props.href, props.as);

const linkPathname = new URL(linkUrl, location.href).pathname;

// Using URL().pathname to get rid of query and hash
const activePathname = new URL(router.asPath, location.href).pathname;
const activePathname = new URL(pathname, location.href).pathname;

const newClassName =
linkPathname === activePathname
Expand All @@ -60,7 +42,7 @@ const ActiveLink = ({
}
}
}, [
router,
pathname,
props.as,
props.href,
activeClassName,
Expand Down
5 changes: 3 additions & 2 deletions examples/active-class-name/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import ActiveLink from "./ActiveLink";

const Nav = () => (
Expand Down Expand Up @@ -31,8 +33,7 @@ const Nav = () => (
<ActiveLink
activeClassName="active"
className="nav-link"
href="/[slug]"
as="/dynamic-route"
href="/dynamic-route"
>
Dynamic Route
</ActiveLink>
Expand Down
16 changes: 9 additions & 7 deletions examples/active-class-name/package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
{
"private": true,
"scripts": {
"dev": "next",
"dev": "next dev",
"build": "next build",
"start": "next start"
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "latest",
"react": "18.2.0",
"react-dom": "18.2.0"
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"typescript": "^4.7.4"
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"typescript": "^5.5.3"
}
}
14 changes: 0 additions & 14 deletions examples/active-class-name/pages/[slug].tsx

This file was deleted.

9 changes: 7 additions & 2 deletions examples/active-class-name/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
"incremental": true,
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

0 comments on commit dadc291

Please sign in to comment.