Skip to content
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Rules if you find yourself adding a third:
- `shinyreact.set_react_page(path=None)` — Express helper; with no args serves `www/index.html` when present, else discovers `www/ui.js` / `www/ui.css` and emits no body HTML. Auto-discovers `HTMLDependency` objects from traditional Shiny renderers and injects the shinyreact dep. `page_opts()` options reach the page (`page_auto()` splats them into our `page_fn`): the no-HTML-file mode honors `title` / `lang` / `theme`; anything else, and any option at all in HTML-file mode, raises a `TypeError` naming it
- `shinyreact.page_react(*args, src_dir=None, js_file="ui.js", css_file="ui.css", title=None, lang="en", **kwargs)` — Core-mode zero-config page: discovers `www/ui.js` / `www/ui.css` next to the calling module, serves them as an mtime-versioned dependency (cache-busted), title defaults to the app folder name; the client appends its own mount container to `<body>`
- `shinyreact.ReactApp(server, *, ui=None, static_assets=MISSING, bookmark_store="disable", **kwargs)` — `shiny.App` for the ui.tsx pattern with the UI discovered next to the calling module: `www/index.html` present → `page_react_html()`, else → `page_react()`. The discovered UI is a per-request function, so bookmark restore works with no wiring (`ReactApp(server, bookmark_store="url")`); `ui=page_react_html(...)` + a bookmark store raises, since one already-built document can't restore. The React asset dir is mounted at `/`, and a passed `static_assets` mapping is **merged with** that mount rather than replacing it — the author wins only on collision (`"/"` key, a bare path, or `None` for nothing). `static_assets` defaults to `MISSING`, not `None`, so `None` can mean "mount nothing". `ui=` overrides discovery (a `page_react_html()` document still gets its dir mounted); discovery reads the *immediate* calling frame, so helpers wrapping `ReactApp(...)` must pass `ui=`
- `shinyreact.page_react_html(path="www/index.html")` — Core-mode helper for apps that own a complete HTML document containing `<meta name="shiny-dependency-placeholder" content="">` (py-shiny's `ui.page_html()` default `deps_replace_pattern=`); Shiny's and shinyreact's tags render in its place. Rarely called directly — `ReactApp` discovers it (plain `shiny.App` works too via `ui.page_html()`, consumed as a git dep on py-shiny `main` until py-shiny#2475 releases — issue #216). Does not auto-discover renderer dependencies
- `shinyreact.page_react_html(path="www/index.html")` — Core-mode helper for apps that own a complete HTML document containing `<meta name="shiny-dependency-placeholder" content="">` (py-shiny's `ui.page_html()` default `deps_replace_pattern=`); Shiny's and shinyreact's tags render in its place. Rarely called directly — `ReactApp` discovers it (plain `shiny.App` works too via `ui.page_html()`, which needs `shiny>=1.8.0`). Does not auto-discover renderer dependencies
- `shinyreact.page_bare(*args, title=None, lang="en", **kwargs)` / `shinyreact.page_react_dep(*, src_dir=None, js_file="ui.js", css_file="ui.css", name=None)` — escape-hatch page builder and app-bundle `HTMLDependency` helper. Neither takes `theme=` or `version=`: styling belongs to the React client, not Bootstrap, and a package needing a fixed version builds its own `HTMLDependency`. `**kwargs` / R's `...` still reach `page_bootstrap()` / `bootstrapPage()`. With no `theme`, `page_bare()` (and so `page_react()`) attaches **no Bootstrap** — jQuery, Shiny's own JS/CSS, and a viewport meta tag, nothing else (#285). Passing a theme restores the plain passthrough
- Bookmark restore + protocol handshake: page entry points emit a `<script type="application/json" id="shinyreact-config">` tag carrying the wire-protocol version and any restored input values (`_bookmark.py` / `bookmark.R`); the bundle asserts the protocol major version and seeds `useShinyInput` initial values from it; the config tag is the only delivery channel (`window.shinyreact._restore` is a write-only DevTools sentinel)

Expand Down
9 changes: 4 additions & 5 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -870,8 +870,8 @@ the shinyreact bundle dependency and the `#shinyreact-config` tag — except
error
- `PageHtmlDocument` is imported from `shiny.ui._page`: py-shiny exports
`page_html()` but not its class
- `shiny.ui.page_html()` arrives with py-shiny#2475, consumed as a git
dependency on py-shiny `main` until it releases
- `shiny.ui.page_html()` arrived with py-shiny#2475, released in shiny
1.8.0 (the pinned minimum)
- `[r]` used directly as `shinyApp(ui = page_react_html())`, implemented by
rewriting the placeholder to `{{ headContent() }}` in memory and calling
`htmltools::htmlTemplate(text_ = ..., document_ = TRUE)`
Expand Down Expand Up @@ -1443,9 +1443,8 @@ initial page.
`page_react`, `page_react_dep`, `page_react_html`, `reactive_output`,
`send_message`, `set_react_page`
- `requires-python` is `>=3.10`; the test matrix covers 3.10–3.14
- it depends on `shiny` and `htmltools>=0.7.0`
- the `shiny` dependency is a **git** reference until py-shiny#2475
releases (issue #216)
- it depends on `shiny>=1.8.0` (the first release with `ui.page_html()`,
py-shiny#2475) and `htmltools>=0.7.0`
- `[r]` `NAMESPACE` exports are exactly: `page_bare`, `page_react`,
`page_react_dep`, `page_react_html`, `reactive_output`, `send_message`,
`wire_tap`
Expand Down
25 changes: 25 additions & 0 deletions pkg-js/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Changelog

## 0.1.1

- First release published through CI with npm trusted publishing
(staged, human-approved). Same code as 0.1.0 plus a README for npm.

## 0.1.0

- Initial npm release as `@posit-dev/shinyreact`. Published by hand to
bootstrap the package; no `js/v0.1.0` tag.

- ESM entry for bundler-tier apps with React externalized as a peer
dependency, plus type declarations and `@posit-dev/shinyreact/styles`.

- Hooks: `useShinyInput`, `useShinyInputValue`, `useSetShinyInput`,
`useShinyOutputValue`, `useShinyOutputStatus`, `useShinyOutputError`,
`useShinyMessageHandler`, `useShinyInitialized`, `useShinyBusy`.

- Components and utilities: `ShinyOutput`, `ImageOutput`,
`ShinyModuleProvider`, `MISSING`.

- Asserts the wire-protocol major version against the server's
`#shinyreact-config` tag at boot, and warns when a page also serves the IIFE
bundle alongside this package.
24 changes: 24 additions & 0 deletions pkg-py/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Changelog

## 0.1.0

* Initial PyPI release.

* Server-side plumbing for the `ui.tsx` pattern: the UI is a React client you
own, and the Shiny server contains only reactive computation.
`@reactive_output` sends JSON to `useShinyOutputValue()` hooks,
`send_message()` reaches `useShinyMessageHandler()`, and `set_react_page()`
(Express), `ReactApp` (Core), `page_react()`, `page_react_html()`,
`page_bare()`, and `page_react_dep()` bootstrap the client.

* Renderer dependencies of traditional Shiny outputs hosted inside a React tree
are discovered automatically, so `ShinyOutput` works with zero configuration.

* Bookmark restore is delivered through the `#shinyreact-config` script tag,
and the client asserts the wire-protocol major version at boot.

* `shinyreact.default` and `shinyreact.asis` input handlers give Python and R
the same view of the JSON a hook sends.

* `shinyreact.playwright.WireTap` records the Shiny websocket for end-to-end
tests.
10 changes: 8 additions & 2 deletions pkg-r/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
Package: shinyreact
Title: Shiny UI Infrastructure for Client-Side React Rendering
Version: 0.0.0.9000
Version: 0.1.0
Authors@R: c(
person("Barret", "Schloerke", , "barret@posit.co", role = c("cre", "aut"),
comment = c(ORCID = "0000-0001-9986-114X")),
person("Winston", "Chang", , "winston@posit.co", role = "ctb",
comment = c(ORCID = "0000-0002-1576-2126")),
person("Garrick", "Aden-Buie", , "garrick@adenbuie.com", role = "ctb",
comment = c(ORCID = "0000-0002-7111-0077")),
person("Carson", "Sievert", , "carson@posit.co", role = "ctb",
comment = c(ORCID = "0000-0002-4958-2844")),
person(, "Posit Software, PBC", role = c("cph", "fnd"),
comment = c(ROR = "03wc8by49"))
)
Description: Server-side plumbing for the 'ui.tsx' pattern in Shiny: the
UI is defined in a client React bundle, and the Shiny server contains
only reactive computation. Ships no UI components.
License: MIT + file LICENSE
URL: https://posit-dev.github.io/shinyreact/r,
URL: https://posit-dev.github.io/shinyreact/r/,
https://github.com/posit-dev/shinyreact
BugReports: https://github.com/posit-dev/shinyreact/issues
Imports:
Expand Down
20 changes: 20 additions & 0 deletions pkg-r/NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# shinyreact 0.1.0

* Initial CRAN release.

* Server-side plumbing for the `ui.tsx` pattern: the UI is a React client you
own, and the Shiny server contains only reactive computation.
`reactive_output()` sends JSON to `useShinyOutputValue()` hooks,
`send_message()` reaches `useShinyMessageHandler()`, and `page_react()` /
`page_react_html()` / `page_bare()` / `page_react_dep()` bootstrap the
client.

* Automatic renderer-dependency discovery: dependencies of traditional Shiny
outputs hosted inside a React tree are pushed to the client after each
flush, so `ShinyOutput` works with zero configuration.

* Bookmark restore is delivered through the `#shinyreact-config` script tag,
and the client asserts the wire-protocol major version at boot.

* `shinyreact.default` and `shinyreact.asis` input handlers give R and Python
the same view of the JSON a hook sends.
5 changes: 4 additions & 1 deletion pkg-r/man/shinyreact-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ version = "0.1.0"
description = "Shiny UI infrastructure for React-based component rendering"
requires-python = ">=3.10"
dependencies = [
"shiny @ git+https://github.com/posit-dev/py-shiny.git@main", # TODO: release pin once py-shiny#2475 ships in a release
"shiny>=1.8.0",
"htmltools>=0.7.0",
]

[tool.hatch.metadata]
# TODO: remove with the git dep once py-shiny#2475 ships in a release.
allow-direct-references = true

[tool.hatch.build.targets.wheel]
packages = [
"pkg-py/src/shinyreact",
Expand Down Expand Up @@ -56,7 +52,7 @@ tests-e2e = [
# one uv cannot find is a warning on every `uv sync`, not an error -- which
# is exactly how a real extras problem would hide.
"pytest-playwright>=0.5.0",
"shiny @ git+https://github.com/posit-dev/py-shiny.git@main",
"shiny>=1.8.0",
"shinywidgets>=0.8.0",
"plotly>=6.7.0",
"pandas>=2.1.0",
Expand Down