Skip to content

Add React interopt guide #886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion data/sidebar_react_latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"context",
"styling",
"router",
"lazy-components"
"lazy-components",
"import-export-reactjs"
],
"Hooks & State Management": [
"hooks-overview",
Expand Down
2 changes: 2 additions & 0 deletions pages/docs/manual/latest/import-from-export-to-js.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ canonical: "/docs/manual/latest/import-from-export-to-js"

You've seen how ReScript's idiomatic [Import & Export](import-export.md) works. This section describes how we work with importing stuff from JavaScript and exporting stuff for JavaScript consumption.

If you're looking for react-specific interop guidance, check out the [React JS Interop guide](../../react/latest/import-export-reactjs.mdx).

**Note**: due to JS ecosystem's module compatibility issues, our advice of keeping your ReScript file's compiled JS output open in a tab applies here **more than ever**, as you don't want to subtly output the wrong JS module import/export code, on top of having to deal with Babel/Webpack/Jest/Node's CommonJS \<-> JavaScript module compatibility shims.

In short: **make sure your bindings below output what you'd have manually written in JS**.
Expand Down
189 changes: 189 additions & 0 deletions pages/docs/react/latest/import-export-reactjs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
title: Import / Export ReactJS
description: "Reusing existing React components"
canonical: "/docs/react/latest/import-export-reactjs"
---

# Import / Export ReactJS

Reusing existing React components in ReScript is straightforward.
This guide will walk you through the steps required to import and use React components within ReScript,
including defining component props and handling various import scenarios.

## Basic Example

To reuse a React component in ReScript, create a new module, specify the component's location, and define its props.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
module Confetti = {
@module("react-confetti") @react.component
external make: (~width: int, ~height: int) => React.element = "default"
}

// Assuming we are in App.res
@react.component
let make = () => {
<Confetti width={300} height={300} />
}
```

```js
import ReactConfetti from "react-confetti";
import * as JsxRuntime from "react/jsx-runtime";

var Confetti = {};

function Playground(props) {
return JsxRuntime.jsx(ReactConfetti, {
width: 300,
height: 300
});
}
```

</CodeTab>

## Importing from Relative Paths

You can import components from relative file paths using the `@module` attribute.
Use "default" to indicate the default export, or specify a named export if needed.

### Named Export Example

<CodeTab labels={["ReScript"]}>

```res
// Equivalent of import { Foo } from "bar"
module Foo = {
@module("bar") @react.component
external make: unit => React.element = "Foo"
}
```

</CodeTab>

## Defining Props Types

You can define a separate type for your component's props within the module.

### Props Type Example

<CodeTab labels={["ReScript", "JS Output"]}>

```res
module Confetti = {
type confettiProps = {
width: int,
height: int,
}

@module("react-confetti") @react.component(: confettiProps)
external make: confettiProps => React.element = "default"
}

@react.component
let make = () => {
<Confetti width={300} height={300} />
}
```

```js
import ReactConfetti from "react-confetti";
import * as JsxRuntime from "react/jsx-runtime";

var Confetti = {};

function Playground(props) {
return JsxRuntime.jsx(ReactConfetti, {
width: 300,
height: 300
});
}
```

</CodeTab>

## Optional Props

To define optional props, use the `?` symbol.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
module Confetti = {
type confettiProps = {
width: int,
height: int,
initialVelocityX?: int,
initialVelocityY?: int,
}

@module("react-confetti") @react.component(: confettiProps)
external make: confettiProps => React.element = "default"
}

@react.component
let make = () => {
<Confetti width={300} height={300} />
}
```

```js
import ReactConfetti from "react-confetti";
import * as JsxRuntime from "react/jsx-runtime";

var Confetti = {};

function Playground(props) {
return JsxRuntime.jsx(ReactConfetti, {
width: 300,
height: 300
});
}
```

</CodeTab>

## Extending Built-in DOM Nodes

To accept existing DOM props for a component, extend the `JsxDOM.domProps` type.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
module Foo = {
type fooProps = {
...JsxDOM.domProps,
customProp: string,
}

@module("foo") @react.component(: fooProps)
external make: fooProps => React.element = "default"
}

@react.component
let make = () => {
<Foo width={"300px"} height={"300px"} customProp="bar" />
}
```

```js
import Foo from "foo";
import * as JsxRuntime from "react/jsx-runtime";

var Foo$1 = {};

function Playground(props) {
return JsxRuntime.jsx(Foo, {
height: "300px",
width: "300px",
customProp: "bar"
});
}
```

</CodeTab>

In this example `width` and `height` can be set because `JsxDOM.domProps` was spread into `fooProps`.