---
title: "Get started with shinyreact"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Get started with shinyreact}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

shinyreact implements the **`ui.tsx` pattern**: the UI lives in a client-side
React codebase, and the Shiny server contains only reactive computation. The
server never renders HTML. It publishes data, and the client decides how to
show it.

The pieces:

1. `page_react()` bootstraps the page. It discovers `www/ui.js` (and
   `www/ui.css`) next to `app.R` and serves them along with `shinyreact.js`,
   which installs React and the hooks at `window.shinyreact`.
2. `reactive_output()` publishes a JSON-serializable value under an output id.
3. `send_message()` pushes a one-off message to the client.
4. On the client, `useShinyInput()` sends values to the server,
   `useShinyOutputValue()` reads what `reactive_output()` published, and
   `useShinyMessageHandler()` receives `send_message()` pushes.

## A minimal app

The app directory holds `app.R` and a `www/` folder:

```
my-app/
├── app.R
└── www/
    ├── ui.js
    └── ui.css   # optional
```

`app.R`:

```{r}
library(shiny)
library(shinyreact)

ui <- page_react() # discovers www/ui.js + www/ui.css

server <- function(input, output, session) {
  output$greeting <- reactive_output({
    paste0("Hello, ", input$name, "!")
  })
}

shinyApp(ui, server)
```

`www/ui.js`, written without a build step. The bundle exposes `React` and
`ReactDOM`, so the client uses `React.createElement` instead of JSX:

```js
const { React, ReactDOM, useShinyInput, useShinyOutputValue } = window.shinyreact;
const h = React.createElement;

function App() {
  const [name, setName] = useShinyInput("name", "world");
  const greeting = useShinyOutputValue("greeting");
  return h(
    "div",
    null,
    h("input", { value: name, onChange: (e) => setName(e.target.value) }),
    h("p", null, greeting)
  );
}

// The page has no mount div; create one and append it to <body>.
const root = ReactDOM.createRoot(
  document.body.appendChild(document.createElement("div"))
);
root.render(h(App));
```

Run it with `shiny::runApp("my-app")`. Typing in the box sends `input$name`
to the server; `output$greeting` recomputes and the paragraph updates.

Apps that want JSX, TypeScript, or npm packages compile `src/ui.tsx` to
`www/ui.js` with a bundler such as Vite. `page_react()` does not care how the
file was produced. The [examples
catalog](https://github.com/posit-dev/shinyreact/blob/main/examples/README.md)
shows each tier, from no-build to Vite + HMR.

## Inputs

`useShinyInput(id, default)` registers a Shiny input and returns
`[value, setValue]`, like `React.useState`. Every `setValue` call is sent to
the server, where it arrives as `input$id`.

Until the client's first value arrives, `input$id` is `NULL`. Guard for that
(or use `req()`) in outputs that depend on it:

```{r}
output$dist <- reactive_output({
  n <- input$bins
  if (is.null(n)) {
    return(NULL)
  }
  hist(faithful$waiting, breaks = n, plot = FALSE)$counts
})
```

Values arrive as the JSON the client sent, with two conveniences: arrays of
scalars become atomic vectors (`c(0, 100)`), and `[]` stays `list()` rather
than becoming `NULL`. Pass `{ type: "shinyreact.asis" }` to `useShinyInput()`
to receive the parsed JSON untouched, or any other Shiny input-handler name
(such as `"shiny.datetime"`) to route the value through that handler.

For action buttons, start at `0` and increment on click, with the debounce
disabled so no click is coalesced:

```js
const [n, setN] = useShinyInput("go", 0, { debounceMs: 0, priority: "event" });
```

## Outputs

`reactive_output()` is assigned to `output$id`. Whatever the expression returns
is serialized with jsonlite and delivered to `useShinyOutputValue("id")` on the
client, unchanged. Return lists for structured data. Use `I()` to keep a
length-one vector as a JSON array:

```{r}
output$dist_data <- reactive_output({
  h <- hist(faithful$waiting, breaks = input$bins, plot = FALSE)
  list(breaks = I(h$breaks), counts = I(h$counts))
})
```

The client can also observe an output's lifecycle with
`useShinyOutputStatus("id")`, which is `"pending"` before the first value,
`"recalculating"` while the server recomputes, and `"ready"` otherwise. Keep
the previous value mounted while recalculating; only show a placeholder when no
value has ever arrived.

## Messages

`send_message()` pushes a payload the client handles once, outside the
reactive output graph:

```{r}
observeEvent(input$save, {
  send_message(session, "notify", list(text = "Saved", level = "info"))
})
```

```js
useShinyMessageHandler("notify", (msg) => toast(msg.text));
```

## Traditional Shiny renderers

Render functions from other packages still work. Assign them to `output$id` as
usual and render them on the client with the `ShinyOutput` component, which
binds the output element inside your React tree:

```{r}
output$plot <- plotly::renderPlotly({
  plotly::plot_ly(x = ~ faithful$waiting, type = "histogram")
})
```

```js
const { ShinyOutput } = window.shinyreact;
h(ShinyOutput, { id: "plot", className: "plotly html-widget html-widget-output" });
```

No `plotlyOutput()` placeholder is needed. shinyreact discovers the renderer's
JavaScript and CSS dependencies from the render function and delivers them to
the client automatically.

## Bookmarking

Pass `enableBookmarking = "url"` (or `"server"`) to `shinyApp()` as usual.
Restored input values are embedded in the page, and `useShinyInput()` uses
them as initial values instead of its default.

## Next steps

- [TSX files and JavaScript build tools](https://posit-dev.github.io/shinyreact/articles/tsx-and-build-tools.html)
  explains `.tsx`, JSX, TypeScript, and what `npm run build` does, for
  readers new to JavaScript tooling.
- [Testing](https://posit-dev.github.io/shinyreact/r/articles/testing.html)
  shows how to assert the JSON a server produces with `shiny::testServer()`,
  and the JSON that crosses the websocket with `wire_tap()`.
- The [JS reference](https://posit-dev.github.io/shinyreact/js/) documents
  every hook and component at `window.shinyreact`.
- [`DESIGN.md`](https://github.com/posit-dev/shinyreact/blob/main/DESIGN.md)
  explains why the pattern looks the way it does.
