Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/keiya01/wc-ssr
SSR with web components
https://github.com/keiya01/wc-ssr
Last synced: 2 months ago
JSON representation
SSR with web components
- Host: GitHub
- URL: https://github.com/keiya01/wc-ssr
- Owner: keiya01
- License: mit
- Created: 2021-04-17T14:07:57.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2021-06-20T02:00:46.000Z (over 3 years ago)
- Last Synced: 2024-09-27T12:08:22.217Z (3 months ago)
- Language: TypeScript
- Homepage:
- Size: 649 KB
- Stars: 15
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# wc-ssr
**NOTE: This library is EXPERIMENTAL.**
`wc-ssr` is a simple Server Side Rendering Library with `Web Components`.
This lib is worked by [Declarative Shadow DOM](https://web.dev/declarative-shadow-dom/). Therefore this lib works to use Server Side Rendering on a supported browser but works to use Client Side Rendering on a not supported browser.## SSR with Web Components
This lib work as SSR in the browser that support [Declarative Shadow DOM](https://web.dev/declarative-shadow-dom/). But this lib perform as Client Side Rendering in not supported browsers.
## Installation
```sh
npm install wc-ssr
```**or**
```sh
yarn add wc-ssr
```## Example
```ts
// client/AddButton/template.tsimport { html, $props, $event, $shadowroot } from "wc-ssr";
type Props = {
title: string;
onClick?: () => void;
};export const template = (props: Props) => html`
${/* You can pass props with `$props()`. */}
${/* You must set shadowroot attribute to use ShadowDOM */}
${/* You can add event with `$event()`. */}
${props.title}
`;```
```ts
// client/AddButton/element.tsimport { BaseElement } from "wc-ssr/client";
import { template } from "./template";export class AddButton extends BaseElement {
constructor() {
super();
}render() {
// `props` is injected to `this.props`.
return template({
title: this.props.title,
onClick: this.props.onClick,
});
}
}customElements.define("add-button", AddButton);
```**NOTE: When you use SSR feature, you can not load `BaseElement` on the server. You must avoid loading `BaseElement` like [this example](https://github.com/keiya01/wc-ssr/blob/master/example/babel.config.js#L10-L17)**. This is because, `BaseElement` inherit `HTMLElement`.
```ts
// client/AddButton/index.tsexport { template } from "./template";
if (IS_CLIENT) {
import(/* webpackMode: "eager" */ "./element");
}
``````ts
// page.tsimport { html } from 'wc-ssr';
import { template as AddButton } from './client/AddButton';export const renderPage = () => html`
Hello World!
${AddButton({ title: 'button', onClick: () => console.log('clicked!') })}
`;
}
``````ts
// server.tsimport fastify, { FastifyInstance } from "fastify";
import { Server, IncomingMessage, ServerResponse } from "http";
import { htmlToString } from "wc-ssr";
import { renderPage } from './page';type App = FastifyInstance<
Server,
IncomingMessage,
ServerResponse,
>;const start = async () => {
const app = fastify();app.get("/example", async (req, reply) => {
reply.header("Content-Type", "text/html; charset=utf-8");
reply.send(`
${htmlToString(renderPage())}
`);
);
});try {
await app.listen(3000);
} catch (err) {
app.log.error(err);
process.exit(1);
}
};start();
```
See detail in [example](https://github.com/keiya01/wc-ssr/tree/master/example).
## Usage
- [ShadowDOM](#ShadowDOM)
- [Styling](#Styling)
- [Props](#Props)
- [Event](#Event)
- [State](#State)
- [Attribute](#Attribute)
- [Lifecycle](#Lifecycle)
- [Hydration](#Hydration)
- [Server Side Rendering](#Server-Side-Rendering)### ShadowDOM
You must use `$shadowroot` method to tell custom element use ShadowDOM.
`$shadowroot` method can take `open` or `closed`.
This is optional. Default value is `open`.```ts
import { html, $shadowroot } from "wc-ssr";export const template = html`
Hello World
`;
```### Styling
You can use css with style tag.
You can set style tag inside template tag.```ts
import { html, $shadowroot } from "wc-ssr";const style = html`
button {
color: red;
}
`;const CustomButton = html`
${style}
button
`;
```You can also set multiple styles as the following.
```ts
const style = html`
div {
background-color: blue;
}
`;
```### Props
You can pass props to component. And you can get props to be injected from BaseElement class.
```ts
import { html, $shadowroot } from "wc-ssr";
import { BaseElement } from "wc-ssr/client";const CustomElement = html`
Hello World
${
PassProps({
text: "This is paragraph",
}) /* Pass props to PassProps component */
}
`;class CustomElement extends BaseElement {
/* ... */
}const PassProps = ({ text }) => html`
${text}
`;class PassProps extends BaseElement {
constructor() {
super();
}render() {
return PassProps({ ...this.props });
}
}
```### Event
You can add event to element by using `$event` method.
```ts
import { html, $shadowroot, $event } from "wc-ssr";
import { BaseElement } from "wc-ssr/client";const EventElement = ({ handleOnClick }) => html`
click me
`;class EventElementClass extends BaseElement {
constructor() {
super();
}handleOnClick = () => {
console.log("Clicked!!");
};render() {
return EventElement({ handleOnClick: this.handleOnClick });
}
}
```### State
You can define state like React. If you defined state and change it, `render()` is executed.
```ts
import { html, $shadowroot, $event } from "wc-ssr";
import { BaseElement } from "wc-ssr/client";type Props = {
items: string[];
text: string;
handleOnChangeText: (e?: InputEvent) => void;
addItem: () => void;
};const DefineState = ({ items, text, handleOnChangeText, addItem }) => html`
- ${item} `)}
${items.map((item) => html`
add item
`;
class DefineState extends BaseElement {
constructor() {
super();
this.state = {
items: [],
text: [],
};
}
addItem = () => {
this.setState({ items: [...this.state.items, this.state.text] });
};
handleOnChangeText = (e?: InputEvent) => {
const target = e?.target as HTMLInputElement;
if (target) {
this.setState({ text: target.value });
}
};
render() {
return DefineState({
items: this.state.items,
text: this.state.text,
handleOnChangeText: this.handleOnChangeText,
addItem: this.addItem,
});
}
}
```
### Attribute
_TODO_
### Lifecycle
You can use [web components lifecycle](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks) like below.
```ts
connectedCallback() {
super.connectedCallback();
console.log('connected!!');
}
```
And you can use additional lifecycle.
- `componentDidMount` ... This function is invoked when all preparation of `BaseElement` is completed.
```ts
componentDidMount() {
super.componentDidMount();
this.setState({ text: this.props.text });
}
```
### Hydration
Hydration is performed automatically by browser.
### Server Side Rendering
You can use `htmlToString` to render html on the server.
```ts
import { htmlToString, html, $shadowroot } from "wc-ssr";
type Props = {
text: string;
};
const render = ({ text }: Props) => html`
${text}
`;
htmlToString(render({ text: "Hello World" }));
```
## License
[MIT](https://github.com/keiya01/wc-ssr/blob/master/LICENSE)