Skip to content

Instantly share code, notes, and snippets.

@maarekj
Created May 8, 2020 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maarekj/96015e983588590707390402d0db3111 to your computer and use it in GitHub Desktop.
Save maarekj/96015e983588590707390402d0db3111 to your computer and use it in GitHub Desktop.
react-helmet binding for bucklescript
[@react.component]
let make = () => {
<div>
<Helmet title="My Title" description="My Description" />
<div> {React.string("My component")} </div>
</div>
};
type htmlAttributes;
type bodyAttributes;
type titleAttributes;
type base;
type style;
type script;
type noscript;
module Meta = {
[@bs.deriving abstract]
type t = {
[@bs.optional]
name: string,
[@bs.optional]
property: string,
[@bs.optional]
content: string,
};
let name = (name, content) => {
t(~name, ~content, ());
};
let property = (property, content) => {
t(~property, ~content, ());
};
let description = content => {
[name("description", content), property("og:description", content)];
};
};
module Link = {
[@bs.deriving abstract]
type t = {
href: string,
[@bs.optional]
hreflang: string,
[@bs.optional]
rel: string,
};
type hreflangType =
| Lang(string)
| LangRegion(string, string)
| XDefault;
type hreflang = {
href: string,
type_: hreflangType,
};
let hreflang = hreflang => {
t(
~href=hreflang.href,
~rel="alternate",
~hreflang=
switch (hreflang.type_) {
| Lang(lang) => lang
| LangRegion(lang, region) => lang ++ "-" ++ region
| XDefault => "x-default"
},
(),
);
};
};
module HelmetJs = {
[@bs.module "react-helmet"] [@react.component]
external make:
(
~defer: bool=?,
~encodeSpecialCharacters: bool=?,
~onChangeClientState: unit => unit=?,
~htmlAttributes: htmlAttributes=?,
~bodyAttributes: bodyAttributes=?,
~titleAttributes: titleAttributes=?,
~defaultTitle: string=?,
~titleTemplate: string=?,
// some <head> tags can be passed as props
~base: base=?,
~title: string=?,
~meta: array(Meta.t)=?,
~link: array(Link.t)=?,
~style: array(style)=?,
~script: array(script)=?,
~noscript: array(noscript)=?,
~children: option(React.element)=?
) =>
React.element =
"Helmet";
};
[@react.component]
let make =
(
~title: option(string)=?,
~hreflangs: list(Link.hreflang)=[],
~description: option(string)=?,
~children: option(React.element)=?,
) => {
let meta =
switch (description) {
| None => None
| Some(desc) => Some(Meta.description(desc)->Belt.List.toArray)
};
let link =
switch (hreflangs) {
| [] => None
| hreflangs => hreflangs->Belt.List.map(Link.hreflang)->Belt.List.toArray->Some
};
<HelmetJs ?title ?meta ?link ?children />;
};
type helmetProp = {
.
[@bs.meth] "toComponent": unit => React.element,
[@bs.meth] "toString": unit => string,
};
type helmet = {
.
"base": helmetProp,
"bodyAttributes": helmetProp,
"htmlAttributes": helmetProp,
"link": helmetProp,
"meta": helmetProp,
"noscript": helmetProp,
"script": helmetProp,
"style": helmetProp,
"title": helmetProp,
};
[@bs.val] [@bs.module "react-helmet"] [@bs.scope "Helmet"] external renderStatic: unit => helmet = "renderStatic";
open Belt;
let html =
(~helmet: option(Helmet.helmet)=?, ~css="", ~mainJs: string, ~appId: string, ~content="", ()) => {
let helmetTitle = helmet->Option.mapWithDefault("", h => h##title##toString());
let helmetMeta = helmet->Option.mapWithDefault("", h => h##meta##toString());
let helmetLink = helmet->Option.mapWithDefault("", h => h##link##toString());
{j|<html class="no-js" lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
$(helmetTitle)
$(helmetMeta)
$(helmetLink)
<style>
$(css)
</style>
</head>
<body>
<div id="$(appId)">
$(content)
</div>
<script src="$(mainJs)"></script>
</body>
</html>|j};
};
let content = ReactDOMServerRe.renderToString(<ComponentUsingHelmet />);
let helmet = Helmet.renderStatic();
let html = Html.html(
~helmet,
~css=".my-css { color: red; }",
~mainJs="https://myjs.com/index.js",
~appId="my-app-content",
~content,
()
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment