Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

render_to_string already renders a template with a Rails view context and returns the generated markup as a string. If a helper is undefined, the usual fix is not to change the rendering method: declare the helper module on the controller with helper, or expose a controller method with helper_method. Rendering through a controller renderer outside an action also requires checking the controller class, Rails configuration, and any request state your helper expects.

What render_to_string actually changes

Rails documents render_to_string as taking the same options as render, but returning a string instead of sending a response to the browser. The method changes the destination of the rendered output; it does not automatically add methods to a template.

Helper visibility is a separate concern. A template can call methods supplied by its view context, helper modules included for that controller, and controller methods explicitly exposed with helper_method. A method that exists on a controller, in a helper module, or only in some other rendering context is not necessarily available everywhere a template is rendered.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start by identifying where the missing method is defined. That determines the correct remedy.

Where the method belongs Use Typical declaration
Reusable presentation logic Controller helper module helper ReportsHelper
Controller-owned state or behavior intentionally used by a view Expose one named controller method helper_method :current_user
Rendering outside an action Use the correct controller renderer and verify context ApplicationController.renderer.render(...)

Make a custom helper available to the controller’s templates

Declare the helper module

Put presentation methods in a helper module and declare that module on the controller whose templates use it:

module ReportsHelper
  def status_badge(report)
    content_tag(:span, report.status.humanize, class: "status-badge")
  end
end

class ReportsController < ApplicationController
  helper ReportsHelper

  def preview
    @report = Report.find(params[:id])
    @html = render_to_string(
      template: "reports/show",
      formats: [:html]
    )
  end
end

The reports/show template can now call status_badge(@report) because the helper is part of the view context created for ReportsController. The returned value is stored in @html; it is not sent as the action response unless you subsequently render or return it.

Declare the module at the appropriate scope

If several controllers need the same helper, declare it on a shared base controller rather than copying declarations:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
class AdminController < ApplicationController
  helper Admin::FormattingHelper
end

Keep the declaration close to the controllers that use it. Do not globally include modules merely to hide a context problem; global inclusion can make dependencies difficult to see and can produce different behavior between controllers.

Expose a controller method with helper_method

helper_method is for a different case: the method is defined on a controller, but a view is intentionally allowed to call it. Declare only the methods that belong in the view interface:

class ApplicationController < ActionController::Base
  helper_method :current_user

  private

  def current_user
    # Application-specific lookup
    @current_user ||= User.find_by(id: session[:user_id])
  end
end

A template can call current_user after this declaration, including when it is rendered with render_to_string through that controller’s normal view context. The method can remain private on the controller; helper_method exposes the named method to views without turning every controller method into a helper.

Do not substitute one mechanism for the other

  • Use helper ReportsHelper for view-focused formatting, links, presentation decisions, and other helper-module behavior.
  • Use helper_method :current_user when the source is controller-owned behavior that the view is explicitly meant to consume.
  • Do not expect helper_method to include an unrelated helper module, or helper to expose arbitrary controller methods.

Render a string from a normal controller action

When you are already inside an action, the simplest path is to call render_to_string on that controller:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
class ReportsController < ApplicationController
  helper ReportsHelper

  def email_preview
    @report = Report.find(params[:id])
    html = render_to_string(
      template: "reports/show",
      layout: "mailer",
      formats: [:html],
      locals: { compact: true }
    )

    ReportMailer.preview(html).deliver_later
    head :accepted
  end
end

The options follow the same rendering rules as render: you can select a template, layout, format, and locals. The important distinction is that the method returns the generated string rather than assigning a response body for the browser.

Use explicit template and format options when more than one template or format could be selected. This makes background jobs, mail previews, and document generation less dependent on controller defaults.

Render outside an action with a controller renderer

For code that is not running during a request, Rails provides a controller renderer. A basic example is:

html = ApplicationController.renderer.render(
  template: "reports/show",
  assigns: { report: report },
  formats: [:html]
)

The exact behavior depends on the controller class used to create the renderer. If ReportsController declares ReportsHelper, rendering through ApplicationController.renderer may not reproduce that controller’s helper setup. Prefer the renderer belonging to the controller that owns the template and declarations:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
html = ReportsController.renderer.render(
  template: "reports/show",
  assigns: { report: report },
  formats: [:html]
)

Some helpers also assume request data such as a host, protocol, session, cookies, current user, or URL options. A renderer used outside a request may not have those values. Supply the required renderer defaults or redesign the helper so that it receives explicit data instead of depending on implicit request state.

Check the Rails version and configuration

Current Rails helper documentation describes all helpers as included by default, while also documenting config.action_controller.include_all_helpers = false for restoring older controller-specific inclusion behavior. Applications can therefore differ according to Rails version and configuration. Inspect the application’s lockfile and environment configuration before assuming a helper should be globally available.

A practical diagnostic sequence

  1. Read the exception exactly. Confirm the missing method name and the template that raised it.
  2. Locate the definition. Decide whether the method is in a helper module, on a controller, or in another object.
  3. Match the declaration. Add helper YourHelper for a module, or helper_method :name for a controller method.
  4. Check the rendering controller. For out-of-action rendering, ensure the renderer is created from the controller that has the declaration.
  5. Check request assumptions. Look for calls involving request, session, cookies, url_for, host, protocol, or authentication state.
  6. Reproduce the real call path. Test the same action or renderer used in production, with the application’s actual Rails version and configuration.

Common failures and fixes

“undefined method” for a helper defined in a module

Cause: The module was not included in the controller’s view context, or the template is rendered through a different controller.

Fix: Declare it with helper ModuleName on the relevant controller and render through that controller. Avoid assuming that a helper declaration on one controller applies to another.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A controller method works in an action but not in the template

Cause: Controller methods are not automatically part of the template’s public interface.

Fix: Add helper_method :method_name. Expose the smallest intentional interface rather than making broad controller internals available.

The action works, but ApplicationController.renderer fails

Cause: The renderer’s controller class or view context differs from the normal action, or the helper expects request state.

Fix: Use ReportsController.renderer (or the controller that owns the view), pass assigns and explicit options, and provide any required URL or request defaults.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Links or paths fail during background rendering

Cause: URL helpers may need a host, protocol, or other request-derived value that is absent outside a request.

Fix: Configure the renderer or call path with the application’s required URL options. If the helper’s output is not inherently request-specific, pass the needed value as an argument instead.

Changing helper configuration appears to do nothing

Cause: The application may be running a different environment, Rails version, or controller configuration than the one you changed.

Fix: Verify the loaded configuration, restart the process, inspect the lockfile, and test the actual controller renderer. Do not infer behavior from a different Rails application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Design and reliability considerations

Keep presentation logic in helpers

Helpers are easiest to reuse when they accept explicit records or values and return presentation output. A helper that reaches deeply into session state, database lookups, or request globals is harder to use from a renderer outside an action. Move data loading into the controller or calling service, then pass that data to the template.

Make rendering deterministic

  • Specify the template and format when multiple variants exist.
  • Pass locals or assigns explicitly for non-request rendering.
  • Use a known layout rather than relying on action defaults when generating mail or documents.
  • Ensure any helper-generated URLs have a deliberate host and protocol.
  • Exercise both the normal action and the out-of-action renderer if both are production paths.

Do not manually instantiate a bare view without a reason

Manually constructing ActionView::Base or globally mixing helper modules can bypass the controller and renderer setup that supplies formats, routes, layouts, and request context. Prefer the documented controller declarations and controller renderer, then investigate the view context if a specialized integration truly requires lower-level control.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If the reason you are producing HTML is to capture a rendered web page as an image or PDF, ScreenshotNeo provides a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF; it is separate from Rails helper visibility, so your Rails rendering fix remains the right solution for templates that need helper methods.

One GET request is enough:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for options and response headers. Equivalent calls:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before capture.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status.
  • An MCP server lets Claude, Cursor, and other MCP clients call screenshot, page-info, and PDF tools.
  • The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account to try it without a card.

Verify the fix

After making the declaration, verify the exact rendering path rather than only checking that the application boots:

html = ReportsController.renderer.render(
  template: "reports/show",
  assigns: { report: Report.first },
  formats: [:html]
)
raise "helper output missing" unless html.include?("status-badge")

This confirms that the controller renderer can build the view context and that the helper’s output appears in the returned string. Adapt the assertion to stable application output; do not rely on incidental whitespace or generated IDs.

Frequently Asked Questions

Does `render_to_string` send an HTTP response?

No. It returns rendered output as a Ruby string; the action must separately render, send, enqueue, or otherwise use that string.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can I call a helper from a controller directly?

Controller-side helper access is a separate facility from template inclusion. Use the controller’s `helpers` proxy when appropriate, but do not treat it as proof that every helper is a controller instance method.

Why does the same template behave differently in a mailer or job?

Those paths may use a different renderer and lack request state. Confirm the controller/view context and provide explicit assigns and URL-related defaults.

Should every controller method be declared with `helper_method`?

No. Expose only controller methods that form an intentional view interface; keep unrelated controller behavior private.

The Bottom Line

Choose the mechanism that matches the method’s owner: declare helper modules with helper, expose controller methods with helper_method, and use the correct controller renderer when rendering outside an action. Then verify request-dependent helpers against the Rails version and configuration your application actually loads.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.