ReasonReact, Auth0 and 3rd Party React Components
Tags: reason, melange, react, webdev
Published: 2023-07-16 12:17:56

One of the ways I like to test a technology is to try and implement something that could be required in a real world app, and authentication is one of those things. I have also been wanting to see how easy it is to use a third-party JS React component with [ReasonReact](https://reasonml.github.io/reason-react/en). So, to kill two birds with one stone I created a simple app that uses the [Auth0](https://auth0.com/) React component. You can view the app [here](https://reason-auth.netlify.app/), and the code is [here](https://github.com/psb/reason-auth).

## Using the `Auth0Provider` component

The login for the app is based on the [Auth0 quick start tutorial](https://auth0.com/docs/quickstart/spa/react/interactive) for React, and the first thing we have to do is wrap our `App` in the `Auth0Provider`. In JavaScript we would do `import { Auth0Provider } from '@auth0/auth0-react';` and wrap our `App` component with the imported `Auth0Provider` component, but in ReasonReact we need to write some bindings to the component using [Melange attributes](https://melange.re/v1.0.0/communicate-with-javascript/#list-of-attributes-and-extension-nodes).

```reasonml
module Auth0Provider = {
  type auth_param = {redirect_uri: string};

  [@bs.module "@auth0/auth0-react"] [@react.component]
  external make:
    (
      ~domain: string,
      ~clientId: string,
      ~authorizationParams: auth_param,
      ~children: React.element
    ) =>
    React.element =
    "Auth0Provider";
};
```

`@bs.module` is a Melange attribute that lets us bind to a value from a JS module. The `= "Auth0Provider"` at the end is the JavaScript name of the value/component we are binding to. The `external` before the `make` is used for JS interop/FFI and is like an FFI `let` binding. Everything else in between is the type signature of a React component in ReasonReact.

In Reason each file is a module, and it is common to have one ReasonReact component per file with the filename being the name of the component. We can also define modules within files which is what we have done for the `Auth0Provider` in the snippet above, and we use the component like so:

```reasonml
<Auth0Provider
  domain="dev-dj37pygvjwvaxjde.us.auth0.com"
  clientId="e6mvq0WBov5jSBW0clTFdAIXgbyyK4gv"
  authorizationParams={redirect_uri: origin ++ "/profile"}>
  <App />
</Auth0Provider>
```

## Using the `useAuth0()` hook

Now that we have wrapped our `App` with the `Auth0Provider` we can start using the `useAuth0()` hook in our components, and to do so we must write some bindings to the hook.

Because in JavaScript we would `import` the hook we again have to use the `@bs.module` attribute to bind to it:

```reasonml
type hook;
[@bs.module "@auth0/auth0-react"] external useAuth0: unit => hook = "useAuth0";
|--------------------1-------------------| |---2---| |-3-|  |-4-|   |----5----|
```

Here we are telling Reason that we are binding to an external something in the module `"@auth0/auth0-react"`(1) and that something is called `"useAuth0"`(5) in JS land; we are binding `"useAuth0"`(5) to the name `useAuth0`(2) in Reason land; and `useAuth`(2) takes a `unit`(3) (i.e., takes no arguments; equivalent to `useAuth0()` in JS) and returns a `hook`(4). We have to give the return value a type (`type hook`) so that the type system knows what we have just "imported"/bound to, and which we need when we want to bind to the values and methods of the hook.

(You don't have to use the same JS name in Reason for a binding. For example, you could do something like:

```reasonml
[@bs.module "@auth0/auth0-react"] external useAuth0Reason: unit => hook = "useAuth0";
```

You would then just use `useAuth0Reason` in your ReasonReact code. In fact, you may need to rename bindings if they clash with reserved key words in Reason.)

The `useAuth0` hook provides various [state values and methods](https://auth0.github.io/auth0-react/functions/useAuth0.html), and to use them we need more bindings.

To call a method we use the `bs.send` attribute:

```reasonml
[@bs.send] external loginWithRedirect: hook => unit = "loginWithRedirect";
```

To get a value we use the `bs.get` attribute:

```reasonml
type user;
[@bs.get] external user: hook => user = "user";
```

Once defined we can use the bindings in our ReasonReact components:

```reasonml
// LoginButton.re
[@react.component]
let make = () => {
  let ua = Bindings.Auth0.useAuth0();

  <button
    className="py-2 px-4 rounded-md bg-blue-500 text-white"
    onClick={_ => Bindings.Auth0.loginWithRedirect(ua)}>
    {React.string("Log In")}
  </button>;
};
```

And the very readable output JavaScript of the LoginButton ReasonReact component:

```js
// Generated by Melange

import * as React from "react";
import * as Auth0React from "@auth0/auth0-react";

function LoginButton(Props) {
  var ua = Auth0React.useAuth0();
  return React.createElement(
    "button",
    {
      className: "py-2 px-4 rounded-md bg-blue-500 text-white",
      onClick: function (param) {
        ua.loginWithRedirect();
      },
    },
    "Log In"
  );
}

var make = LoginButton;

export { make };
/* react Not a pure module */
```

![nice](https://media.giphy.com/media/1jkV5ifEE5EENHESRa/giphy.gif)

Bindings can be written as they are needed, and once written completing the Auth0 quick start guide is pretty straight forward.

## Conclusion

Third-party JS React components can be successfully used in any ReasonReact project. Bindings may look complicated, but once you've written a couple of them you quickly get the hang of it.

---

Cover image by [Miguel Á. Padriñán from Pexels](https://www.pexels.com/photo/close-up-shot-of-keyboard-buttons-2882566/)