emberjs/ember.js · error · Error

Attempted to resolve a dynamic component with a string defin

Error message

Attempted to resolve a dynamic component with a string definition, `${value}` in a strict mode template. In strict mode, using strings to resolve component definitions is prohibited. You can instead import the component definition and use it directly.

What it means

In strict-mode Glimmer/Ember templates, dynamic component invocation (`<Component @x={{...}} />` via `{{component}}` or curried values) cannot resolve components by string name; string resolution requires a runtime resolver, which strict mode forbids. createCurryRef throws when a curried component value is a non-empty string and the template is strict.

Source

Thrown at packages/@glimmer/runtime/lib/references/curry-value.ts:44

) {
  let lastValue: Maybe<Dict> | string, curriedDefinition: object | string | null;

  return createComputeRef(() => {
    let value = valueForRef(inner) as Maybe<Dict> | string;

    if (value === lastValue) {
      return curriedDefinition;
    }

    if (isCurriedType(value, type)) {
      curriedDefinition = args ? curry(type, value, owner, args) : args;
    } else if (type === CURRIED_COMPONENT && typeof value === 'string' && value) {
      // Only components should enter this path, as helpers and modifiers do not
      // support string based resolution

      if (DEBUG) {
        if (isStrict) {
          throw new Error(
            `Attempted to resolve a dynamic component with a string definition, \`${value}\` in a strict mode template. In strict mode, using strings to resolve component definitions is prohibited. You can instead import the component definition and use it directly.`
          );
        }

        let resolvedDefinition =
          expect(
            resolver,
            'BUG: expected resolver for curried component definitions'
          ).lookupComponent?.(value, owner) ?? null;

        if (!resolvedDefinition) {
          throw new Error(
            `Attempted to resolve \`${value}\`, which was expected to be a component, but nothing was found.`
          );
        }
      }

      curriedDefinition = curry(type, value, owner, args);

View on GitHub (pinned to 26f97246a8)

Solutions

  1. Import the component directly and pass its class/definition instead of a string.
  2. Use a lookup map in JS: a plain object keyed by name mapping to imported component definitions.
  3. If string resolution is unavoidable, keep that template in a non-strict (resolver-based) context.

Example fix

// before
this.tabName = 'foo-tab';
{{component this.tabName}}

// after
import FooTab from './foo-tab';
this.tabs = { 'foo-tab': FooTab };
{{component (get this.tabs this.tabName)}}
Defensive patterns

Strategy: type-guard

Validate before calling

import * as Components from './components';
function resolveStrict(name) {
  const C = Components[name];
  if (!C) throw new Error(`Unknown component '${name}' for strict mode`);
  return C;
}
// pass definitions, never strings: {{component (resolveStrict this.name)}}

Type guard

function isComponentDefinition(v) {
  return typeof v === 'function' || (v != null && typeof v === 'object' && !(typeof v === 'string'));
}

Prevention

When it happens

Trigger: Passing a string like `this.componentName = 'foo-bar'` into `{{component this.componentName}}` or `{{#let (component this.name) ...}}` in a template compiled in strict mode.

Common situations: Migrating a classic (resolver-based) Ember app to strict-mode / template-only components / Embroider; dynamic tab or modal renderers that store component names as strings.

Related errors


AI-assisted analysis of emberjs/ember.js@26f97246a8 (2026-09-01). Data as JSON: /api/errors/d476e267f6b6f584. Report an issue: GitHub.