I've been trying to make a Web Component with LitElement that works with server side rendering environments (SSR) like NextJS and VueJS, without the need for any janky client-side-only imports. According to Lit's documentation this should be possible, but I keep running into window is not defined
in NextJS.
What I've got so far is this:
import { render } from '@lit-labs/ssr'
import { TestComponent } from './components/test-component'
const ssrResult = render(TestComponent)
export default ssrResult
Apart from adding a couple of hundred kB to the build the @lit-labs/ssr
-render function seems to do nothing. What I'm trying to achieve, by the way, is not necessarily actual SSR – I'm happy to use any workaround, as long as it's on my end.
The component is structured like this:
import {
html,
LitElement,
TemplateResult
} from 'lit'
import {
customElement,
property,
} from 'lit/decorators.js'
@customElement('test-component')
export class TestComponent extends LitElement {
@property({ type: String })
public title = ''
render(): TemplateResult | void {
return html`
<div class="content">
<h3>${this.title}</h3>
</div>
`
}
}
Here's my rollup config:
import { babel } from '@rollup/plugin-babel'
import commonjs from '@rollup/plugin-commonjs'
import filesize from 'rollup-plugin-filesize'
import nodePolyfills from 'rollup-plugin-polyfill-node'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import terser from '@rollup/plugin-terser'
import typescript from '@rollup/plugin-typescript'
import pkg from './package.json' assert { type: 'json' }
//Node hack
import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url)
global['__filename'] = __filename
const extensions = ['.js', '.jsx', '.ts', '.tsx', '.mjs']
export default {
input: './src/index.ts',
output: [
{
file: pkg.main,
format: 'umd',
name: pkg.name
},
{
file: pkg.module,
format: 'es'
}
],
plugins: [
nodePolyfills(),
nodeResolve({
extensions,
jsnext: true,
module: true,
}),
commonjs(),
typescript({
tsconfig: './tsconfig.json'
}),
babel({
extensions,
exclude: ['./node_modules/**'],
babelHelpers: 'bundled'
}),
filesize(),
terser(),
],
}
As of [email protected]
(https://github.com/lit/lit/releases/tag/lit%402.3.0) it is possible to directly import Lit components into a Next.js project without any client only import workarounds. You do not need to use @lit-labs/ssr
package for that.
So something like below is fine.
// pages/index.tsx
import '../components/test-component';
export default function Home() {
return <test-component />
}
It does not deeply render the custom element's content though, so you'll just get the host custom element tag in the server rendered HTML, though since you mention you're not looking for "actual SSR", perhaps this is all you need.
Once client JS is loaded, the custom element will be upgraded and its contents will render.
You can see an example project here https://github.com/lit/lit/tree/0b35d83ef4f9a72e6520e7a065353c49d44974ac/examples/nextjs
As a side note based on the property you have on your test component, it is best to avoid any global attributes like title
as a property specifier. https://web.dev/custom-elements-best-practices/#attributes-and-properties