Skip to content

Instantly share code, notes, and snippets.

@JTRNS
Created May 30, 2025 04:47
Show Gist options
  • Save JTRNS/f9043b045dcee786fafbf1b376f164d9 to your computer and use it in GitHub Desktop.
Save JTRNS/f9043b045dcee786fafbf1b376f164d9 to your computer and use it in GitHub Desktop.
type safe preact template rendering
import { view } from "./view.ts";
const ListItem = ({ done, task }: { done: boolean; task: string }) => {
return (
<li class={done ? "done" : ""}>
<input type="checkbox" checked={done} />
<span>{task}</span>
<button type="button" class="delete">
Delete
</button>
</li>
);
};
const List = () => {
return (
<ul>
<ListItem done={false} task="Task 1" />
<ListItem done task="Task 2" />
<ListItem done={false} task="Task 3" />
</ul>
);
};
export const TodoView = view({
ListItem,
SomeComponent,
});
TodoView("ListItem") // ERROR: missing props
TodoView("ListItem", { task: todo, done: false }) // OK
TodoView("List") // OK
import { type ComponentType, h } from "preact";
import { renderToString } from "preact-render-to-string"
type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
export function view<T extends Record<string, ComponentType<any>>>(components: T): {
<K extends keyof T>(
key: K,
...args: PropsOf<T[K]> extends object ? [PropsOf<T[K]>] : []
): string;
} {
return ((key, ...props) => {
return renderToString(h(components[key], props[0] ?? null));
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment