Skip to content

Commit

Permalink
fix(react): support Next.js server component (#646)
Browse files Browse the repository at this point in the history
  • Loading branch information
ascorbic authored Apr 28, 2024
1 parent 6b76808 commit a2e58ea
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
site: [astro, lit, preact, qwik, react, solid, svelte, webc]
site: [astro, lit, preact, qwik, react, solid, webc]
steps:
- run: echo Running tests in ${{ matrix.site }}
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/img/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ import logo from "../public/logo.png";
/>
```

> ⚠️ For versions of Next.js before 14.0.0, import from
> ⚠️ For versions of Next.js before 13.5, import from
> `@unpic/react/next-legacy`, not `@unpic/react/nextjs`
### Differences from `next/image`
Expand Down
16 changes: 16 additions & 0 deletions examples/nextjs/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 style={{ margin: 0 }}>{children}</body>
</html>
);
}
45 changes: 45 additions & 0 deletions examples/nextjs/app/rsc/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Head from "next/head";
import { Image } from "@unpic/react/nextjs";
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div>
<Image
src="https://images.unsplash.com/photo-1617718295766-0f839c2853e7"
layout="fullWidth"
alt="fullWidth"
aspectRatio={16 / 9}
priority
/>
<Image
src="https://cdn.shopify.com/static/sample-images/garnished.jpeg"
layout="constrained"
width={800}
height={600}
alt="constrained"
/>
<Image
src="https://bunnyoptimizerdemo.b-cdn.net/bunny7.jpg"
width={800}
height={600}
layout="fixed"
alt="fixed"
/>
<div style={{ height: 10000 }}></div>
<Image
src="https://cdn.shopify.com/static/sample-images/bath.jpeg"
layout="constrained"
width={600}
height={800}
alt="offscreen"
/>
</div>
</>
);
}
6 changes: 3 additions & 3 deletions examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
"@types/react-dom": "^18.2.22",
"@unpic/react": "workspace:^",
"next": "^14.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.4.2"
},
"devDependencies": {
"@netlify/plugin-nextjs": "5.0.0-rc.1"
"@netlify/plugin-nextjs": "^5.0.0"
}
}
28 changes: 23 additions & 5 deletions examples/nextjs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand All @@ -15,9 +19,23 @@
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./*"]
}
"@/*": [
"./*"
]
},
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}
2 changes: 1 addition & 1 deletion packages/react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import logo from "../public/logo.png";
</>;
```

> ⚠️ For versions of Next.js before 14.0.0, import from
> ⚠️ For versions of Next.js before 13.5, import from
> `@unpic/react/next-legacy`, not `@unpic/react/nextjs`
### Differences from `next/image`
Expand Down
2 changes: 1 addition & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"@unpic/core": "0.0.49"
},
"peerDependencies": {
"next": "^14.0.0",
"next": "^13.0.0 || ^14.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
Expand Down
50 changes: 6 additions & 44 deletions packages/react/src/nextjs.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useContext, forwardRef, useMemo, useEffect } from "react";
import { forwardRef, useMemo } from "react";
import type { ImageProps as UnpicImageProps } from "./index";
import { Image as UnpicImage } from "./index";
import { getImageCdnForUrl } from "unpic";
import type { ImageConfigComplete } from "next/dist/shared/lib/image-config.js";
import { imageConfigDefault } from "next/dist/shared/lib/image-config.js";
import { ImageConfigContext } from "next/dist/shared/lib/image-config-context.shared-runtime.js";
import {
imageConfigDefault,
type ImageConfigComplete,
} from "next/dist/shared/lib/image-config.js";

//
const configEnv = process.env
Expand Down Expand Up @@ -32,37 +33,6 @@ export type ImageProps = Omit<UnpicImageProps, "src"> & {
src: string | StaticImport;
};

function checkMatchingPatterns(config: ImageConfigComplete, src: string) {
if (
// match-remote-pattern doesn't support the edge runtime
process.env.NEXT_RUNTIME === "edge" ||
// we don't have access to the image domains/remotePatterns in production
process.env.NODE_ENV !== "development"
) {
return;
}

if (!src?.startsWith("http://") && !src?.startsWith("https://")) {
return;
}
let parsedSrc: URL;
try {
parsedSrc = new URL(src);
} catch (err) {
console.error(err);
return;
}

import("next/dist/shared/lib/match-remote-pattern").then(({ hasMatch }) => {
if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) {
throw new Error(
`[Unpic]: Invalid src (${src}). Images that aren't on a supported image CDN must be configured under images in your \`next.config.js\`\n` +
`See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host`,
);
}
});
}

// Next.js allows various different shapes of the src prop
function getImageData(src: string | StaticImport): StaticImageData | void {
if (typeof src === "string") {
Expand All @@ -79,8 +49,7 @@ export const Image = forwardRef<HTMLImageElement, ImageProps>(
// If using the next/image server we can only serve images with
// the same breakpoints as those in the config

const configContext = useContext(ImageConfigContext);
const config = configEnv || configContext || imageConfigDefault;
const config = configEnv || imageConfigDefault;
const breakpoints = useMemo(() => {
return [...config.deviceSizes, ...config.imageSizes];
}, [config]);
Expand Down Expand Up @@ -131,13 +100,6 @@ export const Image = forwardRef<HTMLImageElement, ImageProps>(

const isRemoteCdn = cdn && cdn !== "nextjs" && cdn !== "vercel";

useEffect(() => {
if (!src || !config || isRemoteCdn) {
return;
}
checkMatchingPatterns(config, src);
}, [src, isRemoteCdn, config]);

// Other image CDNs can use normal Unpic breakpoints
if (isRemoteCdn) {
return <UnpicImage {...childProps} src={src} ref={ref} />;
Expand Down
Loading

0 comments on commit a2e58ea

Please sign in to comment.