This project aims at providing a set of hooks to populate <meta>
, ... for each page. With crawlers now supporting
client-side alterations it's important to support a fallback model for our <head>
tags. The dispatcher located in this
library will always make a queue of how we should fallback, ... This way we'll always have some information to give to a
visiting crawler.
npm i --save hoofd
## OR
yarn add hoofd
import { useMeta, useLink, useLang, useTitle, useTitleTemplate } from 'hoofd';
const App = () => {
// Will set <html lang="en">
useLang('en');
// Will set title to "Welcome to hoofd | π"
useTitleTemplate('%s | π');
useTitle('Welcome to hoofd');
useMeta({ name: 'author', content: 'Jovi De Croock' });
useLink({ rel: 'me', href: 'https://jovidecroock.com' });
return <p>hoofd</p>;
};
Or you can choose to
import { useHead, useLink } from 'hoofd';
const App = () => {
useHead({
title: 'Welcome to hoofd | π',
language: 'en',
metas: [{ name: 'author', content: 'Jovi De Croock' }],
});
useLink({ rel: 'me', href: 'https://jovidecroock.com' });
return <p>hoofd</p>;
};
If you need support for Preact you can import from hoofd/preact
instead.
There's a plugin that hooks in with Gatsby and that
will fill in the meta
, ... in your build process.
This package exports useTitle
, useTitleTemplate
, useMeta
, useLink
and useLang
. These hooks
are used to control information conveyed by the <head>
in an html document.
This hook accepts a string that will be used to set the document.title
, every time the
given string changes it will update the property.
This hook accepts a string, which will be used to format the result of useTitle
whenever
it updates. Similar to react-helmet, the placeholder %s
will be replaced with the title
.
This hook accepts the regular <meta>
properties, being name
, property
, httpEquiv
,
charset
and content
.
These have to be passed as an object and will update when content
changes.
This hook accepts the regular <link>
properties, being rel
, as
, media
,
href
, sizes
and crossorigin
.
This will update within the same useLink
but will never go outside
This hook accepts a string that will be used to set the lang
property on the
base <html>
tag. Every time this string gets updated this will be reflected in the dom.
This hook accepts a few arguments and will lead to an injection of a script tag into the dispatcher (during ssr) or the DOM (during csr).
- src?: this can be a location where the script lives, for example
public/x.js
or an inline script for exampledata:application/javascript,alert("yolo")
. - id?: a unique identifier used for querying the script tag. Atleast one among
src
andid
prop is mandatory. - text?: this sets the inner
text
on the script tag. Can be used for adding embedded data, rich text data. - type?: this sets the
type
attribute on the script tag. - async?: this sets the
async
attribute on the script tag. - defer?: this sets the
defer
attribute on the script tag. - module?: this property will override the
type
atrribute on the script tag with a value ofmodule
. - crossorigin?: 'anonymous' | 'use-credentials';
- integrity?: string;
We expose a method called toStatic
that will return the following properties:
- title, the current
title
dictated by the deepestuseTitleTemplate
anduseTitle
combination - lang, the current
lang
dictated by the deepestuseLang
- metas, an array of unique metas by
keyword
(property, ...) - links, the links aggregated from the render pass.
The reason we pass these as properties is to better support gatsby
, ...
If you need to stringify these you can use the following algo:
const stringify = (title, metas, links) => {
const visited = new Set();
return `
<title>${title}</title>
${metaQueue.reduce((acc, meta) => {
if (!visited.has(meta.charset ? meta.keyword : meta[meta.keyword])) {
visited.add(meta.charset ? meta.keyword : meta[meta.keyword]);
return `${acc}<meta ${meta.keyword}="${meta[meta.keyword]}"${
meta.charset ? '' : ` content="${meta.content}"`
}>`;
}
return acc;
}, '')}
${linkQueue.reduce((acc, link) => {
return `${acc}<link${Object.keys(link).reduce(
(properties, key) => `${properties} ${key}="${link[key]}"`,
''
)}>`;
}, '')}
`;
};
import { toStatic } from 'hoofd';
const reactStuff = renderToString();
const { metas, links, title, lang } = toStatic();
const stringified = stringify(title, metas, links);
const html = `
<!doctype html>
<html ${lang ? `lang="${lang}"` : ''}>
<head>
${stringified}
</head>
<body>
<div id="content">
${reactStuff}
</div>
</body>
</html>
`;
By default this package relies on a statically-initialized context provider to accumulate and
dispatch <head>
and <meta>
changes. In cases where you may want to control the Dispatcher
instance used, this module exports a HoofdProvider
context provider and createDispatcher
function for creating valid context instances.
import { createDispatcher, HoofdProvider } from 'hoofd';
function ssr(App) {
const dispatcher = createDispatcher();
const wrappedApp = (
<HoofdProvider value={dispatcher}>
<App />
</HoofdProvider>
);
const markup = renderToString(wrappedApp);
const { metas, links, title, lang } = dispatcher.toStatic();
// See example above for potential method to consume these static results.
}