I was recently playing with new Fresh framework for deno and it was all great but at some point I realized that there is no possibility to add any additional meta data to page head tag. Basically I want to do 2 things:
Do you have any idea how to achieve this? In the ideal world I would want the possibility to provide my own html template, or at least have some flexible way to manipulate provided fixed template. I did find some code snippet in Fresh source file which is basically before-mentioned fixed html template, but unfortunately it doesn't look customizable to me - only variable element would be opts.headComponents, but I'm unsure if I can affect it.
export interface TemplateOptions {
bodyHtml: string;
headComponents: ComponentChildren[];
imports: (readonly [string, string])[];
styles: string[];
preloads: string[];
lang: string;
}
export function template(opts: TemplateOptions): string {
const page = (
<html lang={opts.lang}>
<head>
<meta charSet="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{opts.preloads.map((src) => <link rel="modulepreload" href={src} />)}
{opts.imports.map(([src, nonce]) => (
<script src={src} nonce={nonce} type="module"></script>
))}
<style
id="__FRSH_STYLE"
dangerouslySetInnerHTML={{ __html: opts.styles.join("\n") }}
/>
{opts.headComponents}
</head>
<body dangerouslySetInnerHTML={{ __html: opts.bodyHtml }} />
</html>
);
return "<!DOCTYPE html>" + renderToString(page);
}
I found the answer in some fresh issue. Edition of head and body tag can be done through JSX, just like this:
/** @jsx h */
import {h} from 'preact';
import {PageProps} from '$fresh/src/server/types.ts';
import {tw} from '@twind';
export const Head = () => (
<head>
<title>My title</title>
</head>
);
export default function LoginPage(props: PageProps) {
return (
<div class={tw`w-screen h-screen flex justify-center bg-green-900`}>
<Head />
</div>
)
}
Guess that way of doing things comes from preact or SSR? From my point of view it's a bit strange - injecting head/body into some divs, not really intuitive.
Edit
Found even better solution, because the answer presented earlier still caused meta tags to be put inside body tag.
We can use _app.tsx which is basically a wrapper for each rendered route and use special Head component from fresh library. And this is the correct way to populate opts.headComponents field from the fixed html template (presented in question). Unfortunately, there seems to be no documentation for that. Example taken from fresh tests:
/** @jsx h */
/** @jsxFrag Fragment */
import { Fragment, h } from "preact";
import { Head } from "$fresh/runtime.ts";
import { AppProps } from "$fresh/server.ts";
export default function App(props: AppProps) {
return (
<>
<Head>
<meta name="description" content="Hello world!" />
</Head>
<props.Component />
</>
);
}