Ember to React Migration Guide

website·Indexed 30 days ago

https://ember-to-react.netlify.app

A comprehensive guide for performing a 1:1 migration from Ember to React. It provides side-by-side comparisons of core concepts including inline logic, whitespace handling, class names, input validation, lifecycle methods, and conditional rendering. The documentation also covers migrating specific Ember addons like ember-svg-jar and ember-concurrency, as well as automation tips using Claude Code skills for HBS to JSX conversion.

Tokens
8.8K
Snippets
44
Records
50
Agent score
75%

What's inside Ember to React

  1. Export React components for use in Ember

    When using the 'React in Ember' framework, React components must be exported using default exports rather than named exports to function correctly. This ensures compatibility with how the framework imports and renders components.
    // Correct: Use default export
    export default FlavorSelector;
    
    // Correct: Use default import
    import FlavorSelector from './flavor-selector';
    
    // INCORRECT: Do not use named exports/imports
    // export { FlavorSelector };
    // import { FlavorSelector } from './flavor-selector';
  2. Migrate Ember lifecycle and state to React hooks

    When migrating from Ember to React, replace Ember's reactive properties and lifecycle modifiers with React hooks.

    Key mapping:

    • @tracked properties $\rightarrow$ useState hook
    • {{did-insert}} modifier $\rightarrow$ useEffect hook with an empty dependency array []
    • @action decorators $\rightarrow$ Regular JavaScript functions
    • Direct property assignment $\rightarrow$ State setter functions (e.g., setLoadingState)
    import { useEffect, useState } from 'react';
    
    function LoadingIndicator() {
      // useState replaces @tracked
      const [loadingState, setLoadingState] = useState('Loading...');
    
      function initComponent() {
        setTimeout(() => {
          setLoadingState('Component initialized');
        }, 2000);
      }
    
      // useEffect with [] replaces {{did-insert}}
      useEffect(() => {
        initComponent();
      }, []);
    
      return <div>{loadingState}</div>;
    }
  3. Render content outside the component hierarchy in React using createPortal

    React uses the createPortal() API from react-dom to render children into a DOM node that exists outside the hierarchy of the parent component. This is commonly used for modals and overlays to ensure they are not clipped by parent containers.
    import { createPortal } from 'react-dom';
    
    function PortalHello() {
      return createPortal(
        <div>Hello cheatsheet!</div>,
        document.getElementById('modal-root')
      );
    }
    
    function Modal({ isOpen, onClose, children }) {
      if (!isOpen) return null;
      
      return createPortal(
        <div className="modal-backdrop" onClick={onClose}>
          <div className="modal-content" onClick={e => e.stopPropagation()}>
            {children}
          </div>
        </div>,
        document.body
      );
    }
  4. Implement conditional rendering in React using JSX

    Conditional rendering in React is performed directly within JSX using JavaScript logic. This allows the use of standard JavaScript if statements, ternary operators, and logical operators to determine which UI elements to return from a component.
    const ComplexLogic = (props) => {
      const { myObject } = props;
    
      if (myObject.something) {
        return <b>This Thing</b>;
      } else if (myObject.thatThing === 'That Thing') {
        return <b>That Thing</b>;
      } else {
        return <b>{myObject.defaultThing}</b>;
      }
    };
    
    // Usage
    const TEST_OBJECT = {
      defaultThing: 'Default render',
      // thatThing: 'That Thing',
      // something: 'just truthy'
    };
    
    render(<ComplexLogic myObject={TEST_OBJECT} />);
  5. Render content outside the component hierarchy in Ember.js

    Ember.js provides ways to render content outside the normal component tree hierarchy (the 'portal' pattern), which is useful for modals, tooltips, and dropdowns to avoid CSS overflow constraints of parent nodes. For modern Ember (v3.20+), use the built-in {{in-element}} helper. For older versions, use the ember-wormhole addon or the ember-elsewhere addon.
    {{#in-element this.destinationElement}}
      <div>Hello cheatsheet!</div>
    {{/in-element}}
    // Component class
    export default class MyComponent extends Component {
      destinationElement = document.getElementById('modal-root');
    }
  6. Group multiple elements in React using Fragments

    React components must return a single root node. To return multiple sibling elements without adding unnecessary nodes to the DOM, use a Fragment (represented by the <> </> syntax).
    function TextContainer() {
      return (
        <>
          Text outside the list
          <ul>
            <li>Text inside the list</li>
          </ul>
        </>
      )
    }
  7. Migrate ember-concurrency tasks to React

    In Ember, ember-concurrency uses generator functions decorated with @task and the perform helper in templates to manage async state (e.g., isRunning) and prevent race conditions. React has no direct equivalent to the perform helper. To migrate this logic to React, move generator-based tasks to standard JavaScript async functions or Promises and replace the perform helper with explicit function calls within event handlers (e.g., onClick). Async state management (loading/error) must be handled manually using React hooks like useState and useEffect, or via libraries such as SWR.
  8. Render nested content in Ember components using {{yield}}

    In Ember, the {{yield}} helper is used within a component template to render the block content passed to it from a parent component. This allows for the creation of flexible, composable components that can wrap arbitrary content.
    {{!-- /components/greeting.hbs --}}
    <p>
      Hello {{yield}}!!!
    </p>
  9. Migrate Ember Portals and SVGs to React

    Replace Ember's {{in-element}} or ember-wormhole with createPortal from react-dom. Replace {{svg-jar}} with standard <img> tags, ensuring alt text is provided.
    // Portal Migration
    import { createPortal } from 'react-dom';
    
    function Modal({ isOpen, onClose, children }) {
      if (!isOpen) return null;
      return createPortal(
        <div className="modal-backdrop" onClick={onClose}>
          <div className="modal-content" onClick={(e) => e.stopPropagation()}>
            {children}
          </div>
        </div>,
        document.body
      );
    }
    
    // SVG Migration
    // {{svg-jar "ice-cream-cone"}} becomes:
    <img alt="" src="assets/ice-cream/ice-cream-cone.svg" />
  10. Convert Ember component state and lifecycle to React

    Replace Ember's @tracked properties and {{did-insert}} modifiers with React's useState and useEffect hooks. Regular functions replace the need for the @action decorator.
    import { useState, useEffect } from 'react';
    
    function LoadingIndicator() {
      // replaces @tracked
      const [loadingState, setLoadingState] = useState('Loading...');
    
      useEffect(() => {
        // replaces {{did-insert}}
        const timer = setTimeout(() => {
          setLoadingState('Component initialized');
        }, 2000);
        return () => clearTimeout(timer);
      }, []);
    
      return <div>{loadingState}</div>;
    }