|
1 |
| -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). |
| 1 | +# Framework Embedded Analtycs in Next.js |
2 | 2 |
|
3 |
| -## Getting Started |
| 3 | +This repository is an example of importing a JS module from a [Observable Cloud](https://observablehq.com/documentation/data-apps/) data app using signed URLs and embedding it in a [Next.js](https://nextjs.org) site using Next.js's App router. The Observable Cloud data app provides charts of the number of medals won by countries in the 2024 Olympic Games, optionally broken down by continent. |
4 | 4 |
|
5 |
| -First, run the development server: |
| 5 | +## Tour |
6 | 6 |
|
7 |
| -```bash |
8 |
| -npm run dev |
9 |
| -# or |
10 |
| -yarn dev |
11 |
| -# or |
12 |
| -pnpm dev |
13 |
| -# or |
14 |
| -bun dev |
| 7 | +The repository uses Observable Cloud's *signed URLs* feature, which enables secure embedding for private data apps. In this example the data app is public, for demonstration purposes. |
| 8 | + |
| 9 | +The entry point for embedding is in [`src/app/page.tsx`](https://github.com/observablehq/nextjs-observable-embed/blob/main/src/app/page.tsx), which renders on the host application server and uses a React component to render the embed. |
| 10 | + |
| 11 | +```tsx |
| 12 | +<ObservableEmbed |
| 13 | + module="https://observablehq.observablehq.cloud/olympian-embeds/medals-chart.js" |
| 14 | + importName="MedalsChart" |
| 15 | +/> |
15 | 16 | ```
|
16 | 17 |
|
17 |
| -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. |
| 18 | +The `ObservableEmbed` component is imported from `src/components/observableEmbed/server.tsx`, and is a server-only component. Because of that it has access to the private key used to make signed URLs. [`jose`](https://github.com/panva/jose) is used to generate the JWT for the signed URL: |
| 19 | + |
| 20 | +```tsx |
| 21 | +const parsedUrl = new URL(url); |
| 22 | +const now = Date.now(); |
| 23 | +const notBefore = now - (now % SIGNATURE_ALIGN_MS); |
| 24 | +const notAfter = notBefore + SIGNATURE_VALIDITY_MS; |
| 25 | +const token = await new SignJWT({"urn:observablehq:path": parsedUrl.pathname}) |
| 26 | + .setProtectedHeader({alg: "EdDSA"}) |
| 27 | + .setSubject("nextjs-example") |
| 28 | + .setNotBefore(notBefore / 1000) |
| 29 | + .setExpirationTime(notAfter / 1000) |
| 30 | + .sign(privateKey); |
| 31 | +``` |
18 | 32 |
|
19 |
| -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. |
| 33 | +This token is passed to the component `ObservableEmbedClient`, imported from `src/components/observableEmbed/client.tsx`. This is a client-only component, responsible for rendering the embed in the browser. It uses the signed URL generated by the Next.js server to import the module from Observable Cloud, and renders the imported component into the DOM: |
20 | 34 |
|
21 |
| -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. |
| 35 | +```tsx |
| 36 | +const ref = useRef<HTMLDivElement | null>(null); |
22 | 37 |
|
23 |
| -## Learn More |
| 38 | +useEffect(() => { |
| 39 | + (async () => { |
| 40 | + const target = ref.current; |
| 41 | + if (!target) return; |
| 42 | + while (target.firstChild) target.removeChild(target.firstChild); |
| 43 | + const mod = await import(/* webpackIgnore: true */ module); |
| 44 | + const component = mod[importName]; |
| 45 | + const element = component instanceof Function ? await component() : component; |
| 46 | + target.append(element); |
| 47 | + })(); |
| 48 | +}, [importName, module]); |
24 | 49 |
|
25 |
| -To learn more about Next.js, take a look at the following resources: |
| 50 | +return <div ref={ref} />; |
| 51 | +``` |
| 52 | + |
| 53 | +## Development |
26 | 54 |
|
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. |
| 55 | +Install dependencies: |
29 | 56 |
|
30 |
| -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! |
| 57 | +```sh |
| 58 | +yarn install |
| 59 | +``` |
31 | 60 |
|
32 |
| -## Deploy on Vercel |
| 61 | +Run the development server: |
33 | 62 |
|
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. |
| 63 | +```sh |
| 64 | +yarn dev |
| 65 | +``` |
35 | 66 |
|
36 |
| -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. |
| 67 | +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. |
0 commit comments