Skip to content

docs(01-hello): rebuild example as Shiny's Old Faithful 01_hello - #207

Merged
schloerke merged 2 commits into
mainfrom
schloerke/example1-oldfaithful-hello
Aug 19, 2026
Merged

docs(01-hello): rebuild example as Shiny's Old Faithful 01_hello#207
schloerke merged 2 commits into
mainfrom
schloerke/example1-oldfaithful-hello

Conversation

@schloerke

@schloerke schloerke commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Rebuilds examples/01-hello as Shiny's canonical 01_hello Old Faithful app, ui.tsx-first. It replaces the previous name-input/click-counter latency demo.

Why this app

01_hello is the app every Shiny user has seen, which makes it the clearest possible place to show what changes under the ui.tsx pattern. Traditional Shiny renders the histogram on the serverrenderPlot({ hist(...) }) ships a PNG and the browser is a passive <img>. Here the server never produces a picture:

  • Server — one reactive_output returning {breaks, counts} as plain JSON, plus a caption. That's the whole server: no plotting library, no image encoding, no plotOutput placeholder.
  • Clientwww/app.js reads that JSON with useShinyOutputValue and draws the bars as SVG <rect>s. Because the chart is real DOM the client owns, it can be styled or made interactive without another round trip.

The slider goes the other way: useShinyInput("bins", 30) pushes to Shiny, which recomputes the counts. Still no JSX, no bundler, no package.json.

Notable bits

  • faithful.py — waiting times plus a stdlib-only equal-width binner, shared by app.py and app-core.py. Keeps the Python side dependency-free (no numpy/matplotlib), which matters for the example that exists to be minimal.
  • faithful.csv — base R's faithful exported so Python needs no data dependency. app.R uses the built-in dataset and hist(..., plot = FALSE).
  • I() in app.R — without it, bins = 1 serializes counts as a scalar instead of a length-1 JSON array and the client crashes. Found by clicking; worth knowing about generally when a reactive_output returns vectors.
  • R returns NULL before the first bins message — Python raises a silent exception instead. req() also works, but its silent error still reaches the client and shows up as a console error, which is a poor look for the flagship example.
  • useShinyOutputStatus — the chart stays mounted while the server recomputes and only dims, per the flicker guidance in CLAUDE.md. Dragging the slider never tears the SVG down.

Relationship to #206

Second commit carries #206's change forward onto the rewritten example: www/index.html is two lines and app.js appends its own mount div to <body>, preparing 01-hello for the upcoming page_react() mode where the server emits no body HTML. #206 has been closed as superseded. Note it was stacked on #198 (schloerke/npm-distribution) while this branches off main — if #198 needs the body-mount change on its own branch, it will have to be reapplied there.

Stacked follow-up: #210

Rewriting this example surfaced a pre-existing bug — Core-mode apps never served their own www/, so app-core.py loaded / and then 404'd on app.js and main.css. That fix lives in #210, stacked on this branch, since it's a separate idea: it touches 10-bookmarking and the package docs, not this example's content.

That means app-core.py is still broken on this branch alone — it works once #210 lands. app.py (Express) and app.R are unaffected and work here.

Verification

Booted all three server entries and drove them in a browser:

  • R and Python produce identical counts at bins 1/2/7/30/50 (sum == 272 throughout), and the bins = 1 edge case renders.
  • document.getElementById("root") is null, with the mount div a direct <body> child.
  • Console clean apart from favicon.ico.

(The app-core.py round trip was verified with #210 applied on top.)

make py-check-tests passes (74); make py-check-types clean; air format, ruff, and prettier applied.

Note when trying it locally: www/app.js is served statically with no cache busting, so a hard refresh is needed after pulling.

@schloerke schloerke changed the title example(01-hello): rebuild as Shiny's Old Faithful 01_hello docs(01-hello): rebuild example as Shiny's Old Faithful 01_hello Aug 19, 2026
Replaces the name/counter latency demo with Shiny's canonical 01_hello app —
a bins slider over the Old Faithful waiting times — rebuilt ui.tsx-first.

The server never renders a picture. One reactive_output returns histogram
{breaks, counts} as plain JSON plus a caption; www/app.js reads it with
useShinyOutputValue and draws the bars as SVG rects. That contrast with
traditional Shiny's renderPlot({ hist(...) }) is the point of the example.

- faithful.py: waiting times + a stdlib-only binner shared by app.py and
  app-core.py, so the Python side stays dependency-free (no numpy/matplotlib)
- faithful.csv: base R's `faithful` exported for the Python servers; app.R
  uses the built-in dataset and hist(..., plot = FALSE)
- app.R wraps the histogram vectors in I() so bins = 1 serializes as a JSON
  array rather than a scalar, and returns NULL until the client's first bins
  message arrives (Python raises a silent exception instead)
- The chart stays mounted while the server recomputes and only dims via
  useShinyOutputStatus, per the repo's flicker guidance

Verified in a browser against app.py, app-core.py, and app.R: R and Python
produce identical counts at bins 1/2/7/30/50, and the console is clean.
@schloerke
schloerke force-pushed the schloerke/example1-oldfaithful-hello branch from 7b18602 to e5bc553 Compare August 19, 2026 20:44
Carries #206's change forward onto the rewritten example. www/index.html is
now two lines (stylesheet + deferred script) and app.js creates its own mount
container via document.body.appendChild(document.createElement("div")) —
safe because the script is deferred, so <body> is parsed when it runs.

This prepares 01-hello for the upcoming page_react() page mode, where the
server emits no body HTML at all and the client owns its mount point.
@schloerke
schloerke force-pushed the schloerke/example1-oldfaithful-hello branch from a6fdcdf to ff6f005 Compare August 19, 2026 20:57
@schloerke
schloerke merged commit c1d399c into main Aug 19, 2026
4 checks passed
@schloerke
schloerke deleted the schloerke/example1-oldfaithful-hello branch August 19, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant