Skip to content

Can not start React or Svelte app on Windows #32

Closed

Description

@zmila

I use
tinyjs new myapp --template react-ts or tinyjs new myapp --template svelte-ts,
then > cd myapp
do > npm install
and run
> tinyjs dev

Empty window is displayed. View source shows
<html><head></head><body></body></html>

Console is empty, no errors, no logs.

However when I do tinjs build and then run .\dist\myapp.exe it shows window with initial react app, with logo, links and all in place.

Creating plain html app with tinyjs new myapp and then tinyjs dev works very good, with auto-recompile and update.

How to fix React or Svelte versions on Windows?

tinyjs version
tinyjs v0.42.3 (txiki.js 26.6.0)

Windows 11 Pro, v 25H2.

Activity

  1. zmila commented on Oct 3, 2026

    @zmila
    Author

    Workaround exists: I copied code from examples/procsy. It works, successfully starting in tinyjs dev mode.

  2. slabbdev commented on Oct 3, 2026

    @slabbdev
    Contributor

    Triage notes from current main (v0.46.0) — a few things to pin this down:

    1. First: retest on v0.46.0 (tinyjs update). You're on v0.42.3 and v0.43–0.46 shipped several Windows-side fixes (env handling, pipe/session changes). If it still reproduces there:

    2. TINYJS_DEBUG=1 tinyjs dev and paste the first ~20 lines. The bridge traces every message crossing the pipe, and the very first ones show what page the launcher was actually told to load — that's the fork in the road between "the URL never reached the launcher" and "the launcher navigated but WebView2 came back empty".

    3. Check the generated entry: .build/dev/entry.js, first lines. It should read:

    const FRONTEND = "http://127.0.0.1:5173";

    If it does, the config handoff is fine and the bug is launcher/navigation-side; if it says something else (or a file path), the devUrl handoff broke on Windows.

    4. While tinyjs dev is running (empty window and all): does http://127.0.0.1:5173 show the React app in a normal browser? And what does the Vite banner say (Local: / Network: URLs)?

    5. About your procsy workaround — that's diagnostic gold. What exactly did you change? If procsy starts the dev server from the backend (tjs.spawn + createApp({ url }) after the port answers), that points at the CLI's probe/spawn ordering on Windows; if it changed how the URL reaches the window, it points at argv/navigation. The diff is the bug's location.

    For what it's worth, I re-read the overlay on current main and it's IPv4-safe already (devUrl: http://127.0.0.1:5173, vite --host 127.0.0.1 --strictPort), so the obvious localhost/::1 suspect is ruled out.

  3. zmila commented on Oct 4, 2026

    @zmila
    Author

    Thanks for so detailed answer!

    1. First, update is not working correctly on Windows as well :)

    tinyjs update
    ==> updating v0.42.3 → v0.46.0
    ==> downloading tinyjs v0.46.0 (windows-x86_64)
    ==> verifying checksum
    iex : The term 'Get-FileHash' is not recognized as the name of a cmdlet, function, script file, or operable program.
    Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:38
    irm https://tinyjs.app/install.ps1 | iex
    ~~~
    CategoryInfo : ObjectNotFound: (Get-FileHash:String) [Invoke-Expression], CommandNotFoundException
    FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.InvokeExpressionCommand

    But normal install is my way:

    irm https://tinyjs.app/install.ps1 | iex
    ==> downloading tinyjs v0.46.0 (windows-x86_64)
    ==> verifying checksum
    ==> installing to C:\Users\zmila\AppData\Local\tinyjs
    ==> installed: tinyjs v0.46.0 (txiki.js 26.6.0)

    2. Launch with debug:

    $env:TINYJS_DEBUG=1; tinyjs dev
    ==> starting frontend dev server: npm run dev

    [email protected] dev
    vite --host 127.0.0.1 --port 5173 --strictPort

    VITE v8.3.2  ready in 206 ms
    
    ➜  Local:   http://127.0.0.1:5173/
    ➜  press h + enter to show help
    

    ==> bundling backend (esbuild)

    GET 1 screens
    EVAL@* window.__emit && window.__emit({"event":"open-files","data":{"paths":["C:\\prg\\tinyjs\\rkt2\\.build\\app\\entry.js"]}})
    << WINSTATE main {"fullscreen":false,"maximized":false,"minimized":false,"focused":true}
    EVAL@* window.__emit && window.__emit({"event":"window-state","data":{"win":"main","fullscreen":false,"maximized":false,"minimized":false,"focused":true}})
    << SYS theme light
    EVAL@* window.__emit && window.__emit({"event":"theme","data":{"dark":false}})
    << GOT 1 [{"id":0,"name":"\\.\DISPLAY1","x":1536,"y":60,"width":1536,"height":960,"visible":{"x":1536,"y":60,"width":1536,"height":912},"scale":1.25,"primary":f…
    << NAVQ n1 main http://127.0.0.1:5173/
    NAVR n1 allow

    3. generated entry: .build/dev/entry.js

    I have only generated entry: .build/**app**/entry.js
    And the file starts with:

    import { createApp } from './bridge.js';
    import * as appMod from './src/main.js';
    
    let FRONTEND;
    try {
      FRONTEND = decodeURIComponent(new URL('./frontend', import.meta.url).pathname);
      if (/^\/[A-Za-z]:\//.test(FRONTEND)) FRONTEND = FRONTEND.slice(1); // windows /C:/…
    } catch { FRONTEND = tjs.exePath.replace(/[\\/][^\\/]*$/, '') + '/frontend'; }
    
    const app = await createApp({
      htmlPath: FRONTEND + '/index.html',
      ...
    

    4. While tinyjs dev is running

    Yes, http://127.0.0.1:5173/ shows usual React initial app in Firefox.
    Sorry, I don't understand And what does the Vite banner say (Local: / Network: URLs).

    5. procsy workaround

    I built and run the procsy sample from the sources - it works ok.
    Then I started comparing the sources and replace some files from procsy to new generated empty react app.
    I copied package.json, and main.tsx, App.tsx from src, and main.tsx from backend.

    PS.
    After several experiments, I see, that removing the creation of menu results in the same empty window, even in working procsy example.

    export function init(_app: TinyApp) {
      (_app as any).setMenu([{ title: 'Help', items: [{ id: 'check-updates', label: 'Check for Updates…' }] }]);
    }
    

    I can run tiny app with empty menu (_app as any).setMenu([{ title: 'Help', items: [] }]); but no menu is not possible.
    Hope, this helps :)

  4. slabbdev commented on Oct 4, 2026

    @slabbdev
    Contributor

    Your trace splits this into three separate things — two real bugs and one config casualty:

    1. Your current empty window is a casualty of the procsy experiment. Your generated entry is the non-devUrl variant:

    htmlPath: FRONTEND + '/index.html'   // FRONTEND = …/.build/app/frontend

    …pointing at .build/app/frontend — a folder tinyjs dev never populates (in dev, sources stay in place; with a devUrl the entry is generated with const FRONTEND = "http://127.0.0.1:5173" instead). After copying files from procsy, your project's tinyjs.json lost its frontend block. Put it back and the window points at Vite again:

    "frontend": {
      "dev": "npm run dev",
      "devUrl": "http://127.0.0.1:5173",
      "build": "npm run build",
      "dist": "dist"
    }

    (Your trace also proves Vite serves fine — NAVQ http://127.0.0.1:5173/ → NAVR allow — and no worries about the banner question: your paste showed it, that's exactly what was needed.)

    2. Your trace surfaced a real tinyjs bug — thank you. open-files fires with entry.js itself as the "opened document" on every dev launch. The backend parses its own argv for files named on the command line (a built-app feature: myapp doc.md), but in dev the process is spawned as tjs run entry.js, so the entry module lands in that parse (run gets dropped as non-existent, entry.js exists → pushed). On Windows/Linux built apps this parse is legitimate; in dev it's noise every launch. I'll open a PR gating that parse on "actually packaged".

    3. tinyjs update failing with Get-FileHash not recognized is a second real bug. The identical script works from your terminal, so the powershell spawned by tinyjs inherits a broken module path. Two things: (a) that code path is being reworked in #35 (download → verify → run from a file), so this lands at the right moment — but the installer itself calls Get-FileHash, so I'll flag the spawned-environment question there; (b) to pin the cause: print $env:PSModulePath from a plain terminal, and again right after the failing tinyjs update, and paste both.

    4. The menu correlation (removing setMenu → empty even in procsy) is the strangest clue — a menu call affecting page rendering smells like a Windows paint-order race. But your experiments are now confounded by 1: the frontend block was already gone. Once restored: two runs with and without the setMenu call, each with the TINYJS_DEBUG=1 trace, and we'll know if it's real.

  5. slabbdev commented on Oct 4, 2026

    @slabbdev
    Contributor

    Two follow-ups to my last comment:

    • The open-files fix is up: fix(runtime): dev backends no longer emit a bogus open-files for their own entry #38. Once it's in a release (or built from this branch), tinyjs dev on your project should stop broadcasting entry.js as an opened document — you're the perfect tester since your trace caught it.
    • One fact from the git history that matters for the original bug: the overlay has written devUrl: http://127.0.0.1:5173 into every --template react-ts project since v0.10.0 — so the project you created on v0.42.3 did start with the right config. That means the very first empty window you saw was not a version problem and not a missing-config problem — which makes the clean reproduction all the more important: restore the frontend block, run tinyjs dev twice (with and without the setMenu call), both with TINYJS_DEBUG=1, and paste both traces. If the empty window comes back with the correct config, those two traces are the whole bug.
  6. zmila commented on Oct 4, 2026

    @zmila
    Author

    Sorry, I'm not sure, that I understand you.

    Both procsy and my apps generated with react-js template have the frontend in tinyjs.json.

    When I create fresh react app , do cd, npm i, and then tinyjs dev - i see blank window.
    But adding this line in main.js does some magic (ex. it triggers lazy loading or initialization of internal state?) and the application is up and running as expected.

    export function init(app: TinyApp) {
    (app as any).setMenu([{ title: 'Help', items: [] }]);
    // window is up; push events with app.push('event', data)
    }

    (Now I'd like to concentrate on adding functionality to my pet project https://github.com/zmila/notaro-r, simple note-taking app).

  7. slabbdev commented on Oct 4, 2026

    @slabbdev
    Contributor

    The proposed fix is up as #39 — and so zmila can test it for real, right now without waiting for a release:

    Test build: https://github.com/slabbdev/tinyjsapp/releases/tag/test-issue32 (launcher-win-test-issue32.zip)

    • built from v0.46.0 + the fix(win): force a present when a navigation lands after the first composition #39 patch, cross-compiled with MinGW-w64 GCC 16.2, same flags as setup.ps1 (static, -mwindows)
    • to test: unzip, replace launcher.exe in your project's dist/ folder with lw-patched.exe, then tinyjs dev on the react template — no setMenu call. If the fix holds, the window paints the app.
    • it's a test build, not a release — the merge build would come from @tarwin's toolchain (or you can rebuild yourself from the branch with the same flags).

    If the window paints, that closes #32's original report end-to-end: the #38 open-files fix handles the trace anomaly, this handles the blank first paint.

  8. tarwin commented on Oct 5, 2026

    @tarwin
    Owner

    Thanks @zmila for the careful reproduction (the setMenu clue was the key), and @slabbdev for the triage. Fixed in v0.48.0.

    The window wasn't failing to paint: the first page load was being dropped. The Windows launcher cancels a page's first http navigation to ask the app's navigation policy, then retries it. On a cold start WebView2 takes about 800 ms to finish that cancel, and the retry, sent before then, was silently ignored, so the window stayed on about:blank. That's why only dev-server templates (react-ts, svelte-ts) showed it; file:// apps aren't asked. Adding a menu just changed the timing. The retry now waits for the cancel to finish.

    v0.48.0 also includes #38, so tinyjs dev no longer reports entry.js as an opened file. Update with tinyjs update (or re-run the install script, since the Get-FileHash problem you hit was in the old update path).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions