Skip to content

Documentation: ExecJS SSR Timer Limitations and Solutions #2299

@justin808

Description

@justin808

Summary

This issue requests documentation for the timer function limitations in ExecJS-based server-side rendering and the solutions available.

The Problem

When using React on Rails with ExecJS for server-side rendering, the following functions are not available or don't work as expected:

  • setTimeout - Logs an error instead of executing
  • setInterval - Logs an error instead of executing
  • clearTimeout - Not functional
  • clearInterval - Not functional

This causes issues with modern React libraries that rely on timers:

  • TanStack Router - Uses timers for route loading
  • React Query/TanStack Query - Uses timers for cache management
  • Many animation libraries - Use timers for transitions
  • Debounce/throttle utilities - Rely on setTimeout

Error Message

When these functions are called during SSR, users see:

[SERVER] setTimeout is not defined for execJS. See https://github.com/sstephenson/execjs#faq

Solutions

1. React on Rails Pro Node Renderer (Recommended)

React on Rails Pro provides a Node.js-based renderer that includes full timer support:

# config/initializers/react_on_rails_pro.rb
ReactOnRailsPro.configure do |config|
  config.server_renderer = "NodeRenderer"
end

Benefits:

  • Full Node.js environment
  • Better performance than ExecJS
  • Hot reload without server restart
  • Support for async operations

2. Custom Polyfills (Workaround)

For libraries that only need basic timer support, polyfills can be added to the server bundle:

// ssr-polyfills.ts - imported at top of server-bundle.js
try {
  const globalEval = eval;
  globalEval(`
    var __ssrTimerId = 0;
    
    setTimeout = function(fn, delay) {
      var id = ++__ssrTimerId;
      if (delay === 0 || delay === undefined) {
        try { fn(); } catch (e) { console.error(e); }
      }
      return id;
    };
    
    clearTimeout = function(id) { /* no-op */ };
    setInterval = function() { return ++__ssrTimerId; };
    clearInterval = function() { /* no-op */ };
  `);
} catch (e) {
  // Fallback to globalThis assignment
}

Limitations of polyfills:

  • Timers execute synchronously (no actual delay)
  • Not all timer behaviors are supported
  • May not work for all libraries

3. Disable SSR for Affected Components

For components that heavily rely on timers, consider client-side only rendering:

<%= react_component("MyComponent", props: @props, prerender: false) %>

Requested Documentation

  1. Add to SSR troubleshooting guide: Document the timer limitation and error message
  2. Add solutions section: Document the three approaches above
  3. Link to React on Rails Pro: Explain Node Renderer benefits
  4. Add example polyfills: For those who can't use Pro

Related

Context

This documentation would help developers who encounter timer-related errors during SSR understand their options and choose the appropriate solution for their use case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Backlog priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions