Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[labs/nextjs, labs/ssr-react, lit/react] Add support for Next.js v14 and App Router #4575

Merged
merged 10 commits into from
Apr 24, 2024
Merged
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @lit-examples/nextjs
# @lit-examples/nextjs-v13

## 0.1.1

Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@lit-examples/nextjs",
"name": "@lit-examples/nextjs-v13",
"version": "0.1.1",
"private": true,
"scripts": {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions examples/nextjs-v14-app/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"root": true,
"extends": "next/core-web-vitals"
}
40 changes: 40 additions & 0 deletions examples/nextjs-v14-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# Internal package links in lockfile will go out of date often.
# External dependency versions are pinned in package.json instead.
package-lock.json
9 changes: 9 additions & 0 deletions examples/nextjs-v14-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Lit in Next.js example
augustjk marked this conversation as resolved.
Show resolved Hide resolved

This is a barebones demonstration of a Web Component authored with Lit working
in a Next.js project with the App Router. You can also see an example of `@lit/react` usage.

It uses the plugin from `@lit-labs/nextjs` to enable deep server rendering of
Lit components.

Note: Both bare custom elements and `@lit/react` wrapped components are placed in files with the `'use client';` directive. Deeply server rendering Lit components with declarative shadow DOM is not supported in React Server Components as React Flight server rendered data will contain the `<template>` element causing React hydration errors, and custom element definitions will not get included with client bundle.
augustjk marked this conversation as resolved.
Show resolved Hide resolved
52 changes: 52 additions & 0 deletions examples/nextjs-v14-app/app/Home.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.container {
padding: 0 2rem;
}

.main {
min-height: 100vh;
padding: 4rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

.title a {
color: #0070f3;
text-decoration: none;
}

.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}

.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}

.title,
.description {
text-align: center;
}

.description {
margin: 4rem 0;
line-height: 1.5;
font-size: 1.5rem;
}

.description a {
color: #0070f3;
text-decoration: none;
}

.description a:hover,
.description a:focus,
.description a:active {
text-decoration: underline;
}
14 changes: 14 additions & 0 deletions examples/nextjs-v14-app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '../styles/globals.css';
import type {Metadata} from 'next';

export const metadata: Metadata = {
title: 'Lit in Next.js App Router',
};

export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
27 changes: 27 additions & 0 deletions examples/nextjs-v14-app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import styles from './Home.module.css';
import SimpleGreeter from './simple-greeter';
import SimpleGreeterReact from '../src/simple-greeter-react';

export default function Home() {
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://lit.dev">Lit</a> and{' '}
<a href="https://nextjs.org">Next.js!</a>
</h1>
<p className={styles.description}>
The component below is a web component #builtWithLit
</p>
<SimpleGreeter name="Friend" />
<p className={styles.description}>
The component below is a component wrapped with{' '}
<a href="https://github.com/lit/lit/tree/main/packages/labs/react">
<code>@lit-labs/react</code>
</a>
</p>
<SimpleGreeterReact name="React" />
</main>
</div>
);
}
7 changes: 7 additions & 0 deletions examples/nextjs-v14-app/app/simple-greeter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use client';

import '../src/simple-greeter';

export default function SimpleGreeter(props: any) {
return <simple-greeter {...props} />;
}
11 changes: 11 additions & 0 deletions examples/nextjs-v14-app/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const withLitSSR = require('@lit-labs/nextjs')({
addDeclarativeShadowDomPolyfill: true,
});

/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
};

module.exports = withLitSSR(nextConfig);
25 changes: 25 additions & 0 deletions examples/nextjs-v14-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@lit-examples/nextjs-v14-app",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@lit-labs/nextjs": "^0.1.2",
"@lit/react": "^1.0.0",
"@types/node": "^18.11.11",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"eslint": "^8.29.0",
"eslint-config-next": "^13.0.6",
"lit": "^3.0.0",
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "~5.3.3"
}
}
13 changes: 13 additions & 0 deletions examples/nextjs-v14-app/public/flame-favicon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions examples/nextjs-v14-app/src/count-display.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

import {LitElement, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';

@customElement('count-display')
export class CountDisplay extends LitElement {
@property({type: Number})
count = 0;
render() {
return html`<p>Count: ${this.count}</p>`;
}
}
17 changes: 17 additions & 0 deletions examples/nextjs-v14-app/src/simple-greeter-react.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

'use client';

import React from 'react';
import {createComponent} from '@lit/react';
import {SimpleGreeter} from './simple-greeter';

export default createComponent({
react: React,
tagName: 'simple-greeter',
elementClass: SimpleGreeter,
});
70 changes: 70 additions & 0 deletions examples/nextjs-v14-app/src/simple-greeter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* @license
* Copyright 2022 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/

import {LitElement, html, css} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import type React from 'react';
import './count-display';

@customElement('simple-greeter')
export class SimpleGreeter extends LitElement {
static styles = css`
div {
border: 1px solid black;
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
}

@media (prefers-color-scheme: dark) {
div {
border-color: white;
}
}

span {
color: rebeccapurple;
}

p {
font-family: sans-serif;
}
`;

@property()
name = 'Somebody';

@property({type: Number})
count = 0;

render() {
return html`
<div>
<h1>Hello, <span>${this.name}</span>!</h1>
<count-display .count=${this.count}></count-display>
<button @click=${() => this.count++}>++</button>
</div>
`;
}
}

declare global {
interface HTMLElementTagNameMap {
'simple-greeter': SimpleGreeter;
}

namespace JSX {
interface IntrinsicElements {
'simple-greeter':
| React.DetailedHTMLProps<
React.HTMLAttributes<SimpleGreeter>,
SimpleGreeter
>
| Partial<SimpleGreeter>;
}
}
}
26 changes: 26 additions & 0 deletions examples/nextjs-v14-app/styles/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

a {
color: inherit;
text-decoration: none;
}

* {
box-sizing: border-box;
}

@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}
27 changes: 27 additions & 0 deletions examples/nextjs-v14-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "es2021",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"experimentalDecorators": true,
"useDefineForClassFields": false,
"plugins": [
{
"name": "next"
}
]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
4 changes: 4 additions & 0 deletions examples/nextjs-v14/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"root": true,
"extends": "next/core-web-vitals"
}