diff --git a/misc_docs/syntax/decorator_jsx_component.mdx b/misc_docs/syntax/decorator_jsx_component.mdx new file mode 100644 index 000000000..3fff8faef --- /dev/null +++ b/misc_docs/syntax/decorator_jsx_component.mdx @@ -0,0 +1,44 @@ +--- +id: "jsx-component-decorator" +keywords: ["jsx", "component", "decorator"] +name: "@jsx.component" +summary: "This is the `@jsx.component` decorator." +category: "decorators" +--- + +**Since 11.1** + +The `@jsx.component` decorator is used to annotate functions that are [JSX](/docs/react/latest/elements-and-jsx) components. This is a _generic_ version that's equivalent to the existing `@react.component` for React. + +You will need this decorator whenever you want to use JSX component in JSX expressions, with a [generic JSX transform](/docs/manual/latest/jsx#generic-jsx-transform-jsx-beyond-react-experimental) configured. + + +### Example + + + +```res +// Assuming `Preact` is set up and configured as a generic JSX transform. +@jsx.component +let make = (~name) => { + +} +``` + +```js +import * as Preact from "preact"; + +function Playground(props) { + return Preact.jsx("button", { + children: "Hello " + props.name + "!" + }); +} + +var make = Playground; +``` + + + +### References + +* [Generic JSX transform](/docs/manual/latest/jsx#generic-jsx-transform-jsx-beyond-react-experimental) diff --git a/pages/docs/manual/latest/jsx.mdx b/pages/docs/manual/latest/jsx.mdx index e4320a515..85e055ba1 100644 --- a/pages/docs/manual/latest/jsx.mdx +++ b/pages/docs/manual/latest/jsx.mdx @@ -230,10 +230,129 @@ Consequently, a JSX component can cram in a few more props before reaching for e **Note** that this is a departure from ReactJS JSX, which does **not** have punning. ReactJS' `` desugars to ``, in order to conform to DOM's idioms and for backward compatibility. -## Tip & Tricks +## Generic JSX transform: JSX beyond React (experimental) -For library authors wanting to take advantage of the JSX: the `@JSX` attribute is a hook for potential ppx macros to spot a function wanting to format as JSX. Once you spot the function, you can turn it into any other expression. +**Since 11.1** -This way, everyone gets to benefit the JSX syntax without needing to opt into a specific library using it, e.g. ReScriptReact. +While ReScript comes with first class support for JSX in React, it's also possible to have ReScript delegate JSX to other frameworks. You do that by configuring a _generic JSX transform_. -JSX calls supports the features of [labeled arguments](function.md#labeled-arguments): optional, explicitly passed optional and optional with default. +This is what you need to do to use a generic JSX transform: +1. Make sure you have a ReScript module that [implements the functions and types necessary for the JSX transform](#implementing-a-generic-jsx-transform-module). +2. Configure `rescript.json` to delegated JSX to that module. + +That's it really. We'll expand on each point below. + +### Configuration +You configure a generic JSX transform by putting any module name in the `module` config of JSX in `rescript.json`. This can be _any valid module name_. Example part from `rescript.json`: + +```json +"jsx": { + "module": "Preact" + }, +``` + +This will now put the `Preact` module in control of the generated JSX calls. The `Preact` module can be defined by anyone - locally in your project, or by a package. As long a it's available in the global scope. The JSX transform will delegate any JSX related code to `Preact`. + +#### What about `@react.component` for components? + +`@react.component` will still be available, and so is a generic `@jsx.component` notation. Both work the same way. + +### Usage Example +Here's a quick usage example (the actual definition of `Preact.res` comes below): + +First, configure `rescript.json`: +```json +"jsx": { + "module": "Preact" + }, +``` + +Now you can build Preact components: +```rescript +// Name.res +@jsx.component // or @react.component if you want +let make = (~name) => Preact.string(`Hello ${name}!`) +``` + +And you can use them just like normal with JSX: +```rescript +let name = +``` + +#### File level configuration +You can configure what JSX transform is used at the file level via `@@jsxConfig`, just like before. Like: +```rescript +@@jsxConfig({module_: "Preact"}) +``` + +This can be convenient if you're mixing different JSX frameworks in the same project. + +### Implementing a generic JSX transform module +Below is a full list of everything you need in a generic JSX transform module, including code comments to clarify. It's an example implementation of a `Preact` transform, so when doing this for other frameworks you'd of course adapt what you import from, and so on. + +> You can easily copy-paste-and-adapt this to your needs if you're creating bindings to a JSX framework. Most often, all you'll need to change is what the `@module("") external` points to, so the runtime calls point to the correct JS module. + +```rescript +// Preact.res +/* Below is a number of aliases to the common `Jsx` module */ +type element = Jsx.element + +type component<'props> = Jsx.component<'props> + +type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> + +@module("preact") +external jsx: (component<'props>, 'props) => element = "jsx" + +@module("preact") +external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx" + +@module("preact") +external jsxs: (component<'props>, 'props) => element = "jsxs" + +@module("preact") +external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs" + +/* These identity functions and static values below are optional, but lets +you move things easily to the `element` type. The only required thing to +define though is `array`, which the JSX transform will output. */ +external array: array => element = "%identity" +@val external null: element = "null" + +external float: float => element = "%identity" +external int: int => element = "%identity" +external string: string => element = "%identity" + +/* These are needed for Fragment (<> ) support */ +type fragmentProps = {children?: element} + +@module("preact") external jsxFragment: component = "Fragment" + +/* The Elements module is the equivalent to the ReactDOM module in React. This holds things relevant to _lowercase_ JSX elements. */ +module Elements = { + /* Here you can control what props lowercase JSX elements should have. + A base that the React JSX transform uses is provided via JsxDOM.domProps, + but you can make this anything. The editor tooling will support + autocompletion etc for your specific type. */ + type props = JsxDOM.domProps + + @module("preact") + external jsx: (string, props) => Jsx.element = "jsx" + + @module("preact") + external div: (string, props) => Jsx.element = "jsx" + + @module("preact") + external jsxKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsx" + + @module("preact") + external jsxs: (string, props) => Jsx.element = "jsxs" + + @module("preact") + external jsxsKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsxs" + + external someElement: element => option = "%identity" +} +``` + +As you can see, most of the things you'll want to implement will be copy paste from the above. But do note that **everything needs to be there unless explicitly noted** or the transform will fail at compile time. \ No newline at end of file