{"id":13525796,"url":"https://github.com/0no-co/hoofd","last_synced_at":"2025-05-15T04:05:11.677Z","repository":{"id":40648492,"uuid":"252484796","full_name":"0no-co/hoofd","owner":"0no-co","description":"Hooks to populate the html head.","archived":false,"fork":false,"pushed_at":"2025-04-11T14:08:12.000Z","size":790,"stargazers_count":338,"open_issues_count":7,"forks_count":15,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-03T18:40:04.511Z","etag":null,"topics":["head","html","react","react-hooks","seo"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0no-co.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"urql-graphql","open_collective":"urql-graphql"}},"created_at":"2020-04-02T14:52:25.000Z","updated_at":"2025-04-10T18:35:51.000Z","dependencies_parsed_at":"2023-02-16T17:31:49.474Z","dependency_job_id":"de02efff-9e83-4faa-a8a4-017cf2e61f9f","html_url":"https://github.com/0no-co/hoofd","commit_stats":null,"previous_names":["jovidecroock/hooked-head","jovidecroock/hoofd"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0no-co%2Fhoofd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0no-co%2Fhoofd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0no-co%2Fhoofd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0no-co%2Fhoofd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0no-co","download_url":"https://codeload.github.com/0no-co/hoofd/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254183301,"owners_count":22028471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["head","html","react","react-hooks","seo"],"created_at":"2024-08-01T06:01:22.188Z","updated_at":"2025-05-15T04:05:06.667Z","avatar_url":"https://github.com/0no-co.png","language":"TypeScript","funding_links":["https://github.com/sponsors/urql-graphql","https://opencollective.com/urql-graphql"],"categories":["Uncategorized","TypeScript"],"sub_categories":["Uncategorized"],"readme":"# Hoofd\n\n[![npm version](https://badgen.net/npm/v/hoofd)](https://www.npmjs.com/package/hoofd)\n[![Bundle size](https://badgen.net/bundlephobia/minzip/hoofd)](https://badgen.net/bundlephobia/minzip/hoofd)\n\nThis project aims at providing a set of hooks to populate `\u003cmeta\u003e`, ... for each page. With crawlers now supporting\nclient-side alterations it's important to support a fallback model for our `\u003chead\u003e` tags. The dispatcher located in this\nlibrary will always make a queue of how we should fallback, ... This way we'll always have some information to give to a\nvisiting crawler.\n\n```sh\nnpm i --save hoofd\n## OR\nyarn add hoofd\n```\n\n```jsx\nimport { useMeta, useLink, useLang, useTitle, useTitleTemplate } from 'hoofd';\n\nconst App = () =\u003e {\n  // Will set \u003chtml lang=\"en\"\u003e\n  useLang('en');\n\n  // Will set title to \"Welcome to hoofd | 💭\"\n  useTitleTemplate('%s | 💭');\n  useTitle('Welcome to hoofd');\n\n  useMeta({ name: 'author', content: 'Jovi De Croock' });\n  useLink({ rel: 'me', href: 'https://jovidecroock.com' });\n\n  return \u003cp\u003ehoofd\u003c/p\u003e;\n};\n```\n\nOr you can choose to\n\n```jsx\nimport { useHead, useLink } from 'hoofd';\n\nconst App = () =\u003e {\n  useHead({\n    title: 'Welcome to hoofd | 💭',\n    language: 'en',\n    metas: [{ name: 'author', content: 'Jovi De Croock' }],\n  });\n  useLink({ rel: 'me', href: 'https://jovidecroock.com' });\n\n  return \u003cp\u003ehoofd\u003c/p\u003e;\n};\n```\n\n## Preact\n\nIf you need support for [Preact](https://preactjs.com/) you can import from `hoofd/preact` instead.\n\n## Gatsby\n\nThere's a plugin that hooks in with [Gatsby](https://www.npmjs.com/package/gatsby-plugin-hoofd) and that\nwill fill in the `meta`, ... in your build process.\n\n## Hooks\n\nThis package exports `useTitle`, `useTitleTemplate`, `useMeta`, `useLink` and `useLang`. These hooks\nare used to control information conveyed by the `\u003chead\u003e` in an html document.\n\n### useTitle\n\nThis hook accepts a string that will be used to set the `document.title`, every time the\ngiven string changes it will update the property.\n\n### useTitleTemplate\n\nThis hook accepts a string, which will be used to format the result of `useTitle` whenever\nit updates. Similar to react-helmet, the placeholder `%s` will be replaced with the `title`.\n\n### useMeta\n\nThis hook accepts the regular `\u003cmeta\u003e` properties, being `name`, `property`, `httpEquiv`,\n`charset` and `content`.\n\nThese have to be passed as an object and will update when `content` changes.\n\n### useLink\n\nThis hook accepts the regular `\u003clink\u003e` properties, being `rel`, `as`, `media`,\n`href`, `sizes` and `crossorigin`.\n\nThis will update within the same `useLink` but will never go outside\n\n### useLang\n\nThis hook accepts a string that will be used to set the `lang` property on the\nbase `\u003chtml\u003e` tag. Every time this string gets updated this will be reflected in the dom.\n\n### useScript\n\nThis hook accepts a few arguments and will lead to an injection of a script tag into the dispatcher (during ssr)\nor the DOM (during csr).\n\n- src?: this can be a location where the script lives, for example `public/x.js` or an inline script for example `data:application/javascript,alert(\"yolo\")`.\n- id?: a unique identifier used for querying the script tag. Atleast one among `src` and `id` prop is mandatory.\n- text?: this sets the inner `text` on the script tag. Can be used for adding [embedded data](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#embedding_data_in_html), [rich text data](https://developers.google.com/search/docs/guides/intro-structured-data).\n- type?: this sets the `type` attribute on the script tag.\n- async?: this sets the `async` attribute on the script tag.\n- defer?: this sets the `defer` attribute on the script tag.\n- module?: this property will override the `type` atrribute on the script tag with a value of `module`.\n- crossorigin?: 'anonymous' | 'use-credentials';\n- integrity?: string;\n\n## SSR\n\nWe expose a method called `toStatic` that will return the following properties:\n\n- title, the current `title` dictated by the deepest `useTitleTemplate` and `useTitle` combination\n- lang, the current `lang` dictated by the deepest `useLang`\n- metas, an array of unique metas by `keyword` (property, ...)\n- links, the links aggregated from the render pass.\n\nThe reason we pass these as properties is to better support `gatsby`, ...\n\nIf you need to stringify these you can use the following algo:\n\n```js\nconst stringify = (title, metas, links) =\u003e {\n  const stringifyTag = (tagName, tags) =\u003e\n    tags.reduce((acc, tag) =\u003e \n      `${acc}\u003c${tagName}${Object.keys(tag).reduce(\n        (properties, key) =\u003e `${properties} ${key}=\"${tag[key]}\"`,\n        ''\n      )}\u003e`\n    , '');\n\n  return `\n    \u003ctitle\u003e${title}\u003c/title\u003e\n\n    ${stringifyTag('meta', metas)}\n    ${stringifyTag('link', links)}\n  `;\n};\n```\n\n```js\nimport { toStatic } from 'hoofd';\n\nconst reactStuff = renderToString();\nconst { metas, links, title, lang } = toStatic();\nconst stringified = stringify(title, metas, links);\n\nconst html = `\n  \u003c!doctype html\u003e\n    \u003chtml ${lang ? `lang=\"${lang}\"` : ''}\u003e\n      \u003chead\u003e\n        ${stringified}\n      \u003c/head\u003e\n      \u003cbody\u003e\n        \u003cdiv id=\"content\"\u003e\n          ${reactStuff}\n        \u003c/div\u003e\n      \u003c/body\u003e\n  \u003c/html\u003e\n`;\n```\n\n### Context API\n\nBy default this package relies on a statically-initialized context provider to accumulate and\ndispatch `\u003chead\u003e` and `\u003cmeta\u003e` changes. In cases where you may want to control the Dispatcher\ninstance used, this module exports a `HoofdProvider` context provider and `createDispatcher`\nfunction for creating valid context instances.\n\n```jsx\nimport { createDispatcher, HoofdProvider } from 'hoofd';\n\nfunction ssr(App) {\n  const dispatcher = createDispatcher();\n  const wrappedApp = (\n    \u003cHoofdProvider value={dispatcher}\u003e\n      \u003cApp /\u003e\n    \u003c/HoofdProvider\u003e\n  );\n  const markup = renderToString(wrappedApp);\n  const { metas, links, title, lang } = dispatcher.toStatic();\n\n  // See example above for potential method to consume these static results.\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0no-co%2Fhoofd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0no-co%2Fhoofd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0no-co%2Fhoofd/lists"}