Available now! The Integrator Free Plan org for developers and integrators is now available. Learn more at the Okta Developer Blog

On this page

Migrate to the third generation (Gen3) of the Sign-In Widget

Identity Engine

This guide explains how to migrate from the second (Gen2) to the third generation (Gen3) of the Okta Sign-In Widget.

Note: This document is only for Okta Identity Engine. See Identify your Okta solution (opens new window) to determine your Okta version.


Learning outcomes

  • Upgrade from Gen2 to Gen3 of the Sign-In Widget.
  • Use design tokens to adapt your existing customizations to Gen3.
  • Use the recommended functions for Gen3.
  • Roll back to the second generation, if necessary, without affecting other code in your Sign-In Widget template.

What you need

Sample code

Gen3 design tokens - customization examples


About customizations of Gen3

Gen3 of the Sign-In Widget offers the same user experience as the second generation.

However, it adds accessibility improvements in color contrast, focus management, and screen reader behavior. Gen3 also sets a foundation for improvements in customization and globalization.

Still, there are a few key differences between Gen2 and Gen3. Consider the following:

See Sign-In Widget, third generation (opens new window) to verify if the third generation is right for your org.

CSS and design tokens

Unlike Gen2, Gen3 of the Sign-In Widget doesn’t use fixed CSS class names (className). See Custom buttons for an example.

You can still use CSS to style Gen3 of the Sign-In Widget. However, without fixed className selectors, you can’t reliably style widget elements with className-based overrides.

Instead, Gen3 uses design tokens.

Design tokens make the Sign-In Widget's visual style consistent and easier to update. Tokens replace static values to customize the following:

  • Color
  • Border
  • Font family
  • Font size
  • Font weight
  • Line height
  • Spacing

See Design token customization examples.

Note: Gen3 also offers autogenerated palettes that preserve accessible color contrast ratios. See the Odyssey by Okta design system (opens new window) and Palette generation and accessibility.

jQuery and Preact

Gen3 is built on Preact (opens new window), a lightweight React Native (opens new window) alternative. The architecture changes from jQuery-based (Gen2) to Preact-based (Gen3). As a result, each generation of the Sign-In Widget handles DOM manipulations in a different way.

For example, the afterRender function doesn’t work in Gen3 as it does in Gen2. See About afterTransform and afterRender for more information.

Upgrade to Gen3

You can upgrade to Gen3 in a few different ways:

Note: If you have a new Identity Engine org, Gen3 isn't enabled by default.

Use the Admin Console

To upgrade to Gen3:

  1. In the Admin Console, go to Customizations > Brands.
  2. Select a brand.
  3. Go to the Pages tab, and then click Edit or Configure for the Sign-in page.
  4. Click the Settings tab.
  5. In the Sign-In Widget version section, click Edit.
  6. Turn on the Use the third generation toggle.
  7. Click Save to draft, and then click Publish.

See Brands for more details about brands in Okta.

Use the API

Send a PUT request to the https://{yourOktaDomain}/api/v1/brands/{brandId}/pages/sign-in/customized endpoint. Include the following:

  • Provide a value for the domain:
  • Provide a value for brandId. Ensure that it’s the ID associated with the domain or custom domain that you want to upgrade.
  • In the widgetCustomizations object, set widgetGeneration to G3. See widgetGeneration (opens new window).

See Replace the customized sign-in page (opens new window) for more details.

Add design tokens to your code

Use the code editor to add design tokens to your code. See Use the code editor (Gen3).

Design token examples

Pass the design token values into the OktaSignIn constructor. For example:

new OktaSignIn({
  theme: {
    tokens: {
      PalettePrimaryMain: '#D11DCA',
      TypographyColorBody: '#00297A',
      TypographyColorHeading: '#00297A',
      TypographyFamilyHeading: 'Helvetica',
      TypographyFamilyBody: 'Helvetica',
      TypographyWeightHeading: 600,
      BorderRadiusMain: '24px',
      Spacing5: '2.85714286rem',
    }
  }
});

See Customization examples for more information.

Use the useSiwGen3 variable

The useSiwGen3 variable is a conditional that allows you to set behaviors for a migration to Gen3.

Use useSiwGen3 to add specific code that works when you enable Gen3, but doesn't affect other code in the template if you need to roll back to Gen2.

Note: If your org uses Classic Engine, the useSiwGen3 variable appears in the code editor, but you can't use it.

The following example shows how to style the okta-login-container for a migration to Gen3:

{{#useSiwGen3}}
    <style nonce="{{nonceValue}}">
        #okta-login-container {
            background-color: red !important;
        }
    </style>
{{/useSiwGen3}}

About afterTransform and afterRender

The afterRender (opens new window) function doesn’t work with Gen3 as it does with Gen2. In Gen3, if you use afterRender for DOM manipulations, the Okta Sign-In Widget reverts any customizations to default settings. See jQuery and Preact.

The afterTransform function is the recommended way to apply DOM customizations in Gen3.

To use the afterTransform function, see Use the afterTransform function (recommended). To keep using the afterRender function, see Use the afterRender function (not recommended).

Gen3 of the Sign-In Widget introduces a new function: afterTransform(). See jQuery and Preact.

The function takes two arguments:

  • The name of the form to customize
  • A function that receives a context argument with the changes

signIn.afterTransform('form_name', function (context) { }

The afterTransform function doesn’t update the DOM after components are already rendered. Instead, it allows you to modify the formBag object sent to the components. The formBag controls what the components render.

Consider the following examples:

Note: Supplying a wildcard (*) for the form_name matches all forms. You can't use a wildcard for partial name matches.

Change button text examples

The following example shows how to change the text of the Submit button to Log in on the Identify page:

oktaSignIn.afterTransform('identify', ({ formBag }) => {
 const submitIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Button' && ele.options.type === 'submit');
 if (submitIndex != -1) {
   const submit = formBag.uischema.elements[submitIndex];
   submit.label = 'Login';
 }
});

The following example shows how to change the text of the Submit button to Register on the Enroll profile page:

oktaSignIn.afterTransform('enroll-profile', ({ formBag }) => {
   const submitIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Button' && ele.options.type === 'submit');
   if (submitIndex != -1) {
       const submit = formBag.uischema.elements[submitIndex];
       submit.label = 'Register';
   }
});

The following example shows how to remove the Help, Unlock account?, and Forgot password? links from the Identify page:

oktaSignIn.afterTransform('identify', ({ formBag }) => {
 const help = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'help');
 const unlock = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'unlock');
 const forgot = formBag.uischema.elements.find(ele => ele.type === 'Link' && ele.options.dataSe === 'forgot-password');
 formBag.uischema.elements = formBag.uischema.elements.filter(ele => ![help, unlock, forgot].includes(ele));
});

Add an instructional paragraph example

The following example shows how to add a custom description to the Password recovery page:

oktaSignIn.afterTransform?.('identify-recovery', ({ formBag }) => {
   const titleIndex = formBag.uischema.elements.findIndex(ele => ele.type === 'Title');
   // Add custom description after title
   const descr = {
       type: 'Description',
       contentType: 'subtitle',
       options: {
           variant: 'body1',
           content: '<div class=\'my-reset-description\'>Description<br />about<br />recovery</div>'
       },
   };
   if (titleIndex != -1) {
       formBag.uischema.elements.splice(titleIndex + 1, 0, descr);
   }
});

For Gen3, the afterRender function doesn't work when used to make DOM manipulations and other render-related side effects. If you use afterRender for DOM manipulations, Gen3 reverts any customizations to default settings. See jQuery and Preact and Components and Hooks must be pure (opens new window).

If you still want to use afterRender, consider the following:

Use MutationObserver for DOM manipulations

To prevent Gen3 of the Sign-In Widget from reverting your afterRender customizations, use the DOM MutationObserver function. See MutationObserver (opens new window).

To update UI elements, consider the following example:

<script type="text/javascript" nonce="{{nonceValue}}">
    var config = OktaUtil.getSignInWidgetConfig();

    var oktaSiwRoot = document.querySelector('#okta-login-container');
    // The following allows you to reference the context from each render
    var contextObj = {};
    function cb(mutations, observer) {
      // For the primary auth form, updates the button label
      if (contextObj.formName === 'identify') {
        var el = document.querySelector('[data-type="save"]');
        if (el) { el.textContent = 'Some new label'; }
      }
      // For the reset-authenticator view, updates the button label
      if (contextObj.formName === 'reset-authenticator') {
        var el = document.querySelector('[data-type="save"]');
        if (el) { el.textContent = 'A different label'; }
      }
    }
    // Initializes the mutation observer object
    var observer = new MutationObserver(cb);

    // Renders the Okta Sign-In Widget
    var oktaSignIn = new OktaSignIn(config);

    // The following varies based on your configuration
    oktaSignIn.renderEl({ el: '#okta-login-container' }, OktaUtil.completeLogin, function (error) {
       console.log(error.message, error);
    });

    oktaSignIn.on('afterRender', function (ctx) { // ← Restores the context
      // Resets the global context object for reference using the callback function
      contextObj = ctx;
      // The following condition only executes the observer for specific views/forms
      if (ctx.formName === 'identify' || ctx.formName === 'reset-authenticator') {
        // Pauses
        observer.disconnect();

        // Calls once after initial render
        cb();

        // Observes for re-renders
        observer.observe(oktaSiwRoot, {
          subtree: true,
          childList: true,
          attributes: true,
          characterData: true,
        });
      }
    });
 </script>

Use afterRender for non-DOM manipulations

The third generation can use the afterRender function for non-DOM manipulations without extra logic. The following example doesn't update the UI, so it doesn't need the MutationObserver. Instead, it sends a log to your external logging service:

<script type="text/javascript" nonce="{{nonceValue}}">
   var config = OktaUtil.getSignInWidgetConfig();

   // Renders the Okta Sign-In Widget
   var oktaSignIn = new OktaSignIn(config);

   // The following varies based on your own configuration
   oktaSignIn.renderEl({ el: '#okta-login-container' }, OktaUtil.completeLogin, function (error) {
      console.log(error.message, error);
   });

   oktaSignIn.on('afterRender', function (context) {
      if (context.formName === 'identify') {
         // Sends a log to your external logging service indicating a customer landed on this view
         someExternalLoggingService.log('Rendered Primary auth form'); 
      }
   });
</script>

See also