🎨Customize Widget

Customize the look and feel of the widget to match the design of your dApp and suit your needs

LI.FI Widget supports visual customization, allowing you to match your web app's design. The widget's layout stays consistent, but you can modify colors, fonts, border radius, container styles, disable or hide parts of the UI, and more.

Start customizing the widget by tweaking some of the following options:

interface WidgetConfig {
  // sets default appearance - light, dark, or auto
  appearance?: Appearance;
  // disables parts of the UI
  disabledUI?: DisabledUIType[];
  // hides parts of the UI
  hiddenUI?: HiddenUIType[];
  // makes parts of the UI required
  requiredUI?: RequiredUIType[];
  // tweaks container, components, colors, fonts, border-radius
  theme?: WidgetTheme;
}

Check out our LI.FI Playground to play with customization options in real time.

Theme

By customizing the theme, you can ensure the LI.FI Widget matches the look and feel of your application, providing a seamless user experience.

The theme configuration option allows you to customize various aspects of the widget's appearance, including colors, typography, shapes, and component styles.

Container

The container option customizes the main container of the widget. In the example below, we adjust the border and border-radius properties of the container.

import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';

export const WidgetPage = () => {
  const widgetConfig: WidgetConfig = useMemo(() => ({
      theme: {
        container: {
          boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
          borderRadius: '16px',
        },
      },
    }), []);

  return (
    <LiFiWidget integrator="Your dApp/company name" config={widgetConfig} />
  );
};

Palette, shape, typography

The palette option defines the color palette for the widget. You can customize the background colors, greyscale colors, primary and secondary colors, and text colors.

The shape option defines border-radius overrides for all elements in the widget.

The typography option customizes the font settings like font families.

Let's proceed with the theme and adjust the primary and secondary colors of the inner elements, along with the font family and border-radius.

import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';

export const WidgetPage = () => {
  const widgetConfig: WidgetConfig = useMemo(() => ({
      theme: {
        palette: {
          primary: { main: '#7B3FE4' },
          secondary: { main: '#F5B5FF' },
        },
        shape: {
          borderRadius: 0,
          borderRadiusSecondary: 0,
        },
        typography: {
          fontFamily: 'Comic Sans MS',
        },
        container: {
          boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
          borderRadius: '16px',
        },
      },
    }), []);

  return (
    <LiFiWidget integrator="Your dApp/company name" config={widgetConfig} />
  );
};

Components

The components option allows you to customize the styles of specific components within the widget.

The current list of available components with more to come:

  • MuiAppBar is used as a header/navigation component at the top of the widget.

  • MuiAvatar is used to display token/chain avatars.

  • MuiButton is used for various buttons in the widget.

  • MuiCard is used for card elements within the widget. There are also three default card variants available for customization: outlined, elevation, and filled. They can be set using defaultProps option (see example below).

    • outlined - default variant where the card has thin borders.

    • elevation - variant where the card has a shadow.

    • filled - variant where the card is filled with color (palette.background.paper property).

  • MuiIconButton is used for icon buttons within the widget.

  • MuiInputCard is used for input cards within the widget.

  • MuiTabs is used for tab navigation within the widget (available in split subvariant).

With the components option, each component can be customized using the MUI's styleOverrides property, allowing for granular control over its styling.

Let's take a look at the example, which shows how we can use card component variants together with overriding tabs component styles.

import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';

export const WidgetPage = () => {
  const widgetConfig: WidgetConfig = useMemo(() => ({
      palette: {
        primary: {
          main: '#006Eff',
        },
        secondary: {
          main: '#FFC800',
        },
        background: {
          default: '#ffffff',
          paper: '#f8f8fa',
        },
        text: {
          primary: '#00070F',
          secondary: '#6A7481',
        },
        grey: {
          200: '#EEEFF2',
          300: '#D5DAE1',
          700: '#555B62',
          800: '#373F48',
        },
      },
      shape: {
        borderRadius: 12,
        borderRadiusSecondary: 12,
        borderRadiusTertiary: 24,
      },
      container: {
        boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
        borderRadius: '16px',
      },
      components: {
        MuiCard: {
          defaultProps: { variant: 'filled' },
        },
        // Used only for 'split' subvariant and can be safely removed if not used
        MuiTabs: {
          styleOverrides: {
            root: {
              backgroundColor: '#f8f8fa',
              [`.${tabsClasses.indicator}`]: {
                backgroundColor: '#ffffff',
              },
            },
          },
        },
      },
    }), []);

  return (
    <LiFiWidget integrator="Your dApp/company name" config={widgetConfig} />
  );
};

Pre-configured Themes

The LI.FI Widget includes several pre-configured themes that provide a starting point for customization. These themes demonstrate various configurations of colors, shapes, and component styles, giving you an idea of how the widget can be styled to fit different design requirements.

Check out our LI.FI Playground to play with themes.

Besides the default theme, there are three pre-configured themes available with more to come.

Developers can import them directly from @lifi/widget package.

import { azureLightTheme, watermelonLightTheme, windows95Theme } from '@lifi/widget';

Watermelon

Azure

Windows 95

Customizing Pre-configured Themes

You can further customize these pre-configured themes by modifying their properties or combining them with your own custom styles. This flexibility allows you to achieve the exact look and feel you desire for your application.

Appearance

The widget has complete dark mode support out of the box. By default, the appearance option is set to auto, matching the user's system settings for dark and light modes.

Now, let's set the default appearance to dark.

import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';

export const WidgetPage = () => {
  const widgetConfig: WidgetConfig = useMemo(() => ({
      appearance: 'dark',
    }), []);

  return (
    <LiFiWidget integrator="Your dApp/company name" config={widgetConfig} />
  );
};

Layout

There are 4 ways of dealing with the widget's height in terms of layout: default, restricted max height, restricted height, full height.

Note that none of these layouts are intend for use with the drawer variant. Default, restricted max height, and restricted height can be used with both wide and compact variants. Full height should only be used with the compact variant.

Default

By default the widget will have a maximum height of 686px. This requires no change to the config but fundamentally works in the same way as the restricted max height.

Restricted Max Height

In restricted max height layout, pages within the widget will occupy only the minimum amount of space needed - the Widgets height will contract and expand but pages shouldn't increase beyond the stated max height. Any pages larger than the max height will be scrollable to allow content to be reached.

You can set the max height on the theme's container object in the config - this should be a number (the number of pixels) and not a string. And its advisable to use a value above 686 which is the default value of the widget's height.

import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  theme: {
    container: {
      maxHeight: 820
    }
  }
}

Restricted Height

In restricted height layout, pages within the widget will always occupy the full height stated in the config. When navigating through different pages the height of the widget should remain consistent. Any pages larger than the stated height will be scrollable to allow content to be reached.

You can set the height on the theme's container object in the config - this should be a number (the number of pixels) and not a string. And its advisable to use a value above 686 which is the default value of the widget's height.

import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  theme: {
    container: {
      height: 900
    }
  }
}

Its not recommend to attempt to use the containers height and maxHeight together - in the widget they present different layout modes.

Full Height

Full height has been included to better present the widget where less screen real estate is available such as on mobile devices. It assumes that the widget will make up the majority of the content and functionality on a page and where possible will allow scrolling via the page itself.

With this layout the widget will attempt to occupy the full height of the HTML element containing it.

Full height layout can feature number of different properties in the config - we will break each of these down.

import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  variant: 'compact'
  theme; {
    container: {
      display: 'flex',
      height: '100%'
    }
    header: {
      position: 'fixed',
      top: 0,
    },
  }
}
  • variant should be set as 'compact' - 'compact' itself is already built to work with smaller screen spaces in mind.

  • theme.container - this should feature display: 'flex' and height: '100%' to instruct the widget to occupy the full space of the containing HTML element and to also allow the use of flex layout in some parts of the widget to try to better use available screen space.

  • theme.header - this is optional.

    • When adding theme.header you should state both position: 'fixed' and a top value (above we use top: 0)

      • This will make widget's header behavior like a sticky header which stays in a fixed position when other parts of the widgets pages are still scrollable.

      • Setting the top value means that you can account for the position of any elements you might have on your page above the widget. For example if you have a navigation bar that sits above the widget that is 60 pixels in height you should set the top value to top: 60

    • Without theme.header the widget's header will be scroll with the rest of the widget pages content.

Considerations when using Full Height

To present the widget for a mobile experience the pages HTML & CSS external to the widget will also have to be considered in addition to the the widget config. In Full Height layout the widget surrenders its height to its external HTML container so container needs to be handled well by the containing application. Here are some things to think about when implementing Full Height layout.

  • Viewport meta may need to be updated for the page to present correctly

    • e.g. <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

  • You may also want to think about how the page behaves in terms of occupying the fully page height. Here is how we do it in the Widget Playground.

    • In the Widget Playground we use 100dvh with the min-height css which means that if widgets pages are bigger than this that the page can still scroll to allow access to that widget content. Also this means that when widget's pages are smaller than the viewport they can scale and position elements using flex to better use the available space to occupy the full screen height.

    • In CSS you should add overscroll-behavior: none; to the root/body of your page to prevent undesired scrolling behavior.

    • Placement on the page in relation to site navigation, header and footers may also have to be considered. We have mocked this experience in the Widget Playground. To see it:

      • Go to https://playground.li.fi/ and select Full Height from the Layout Options.

      • Open the Playground Settings and you should be able to toggle the 'show mock header' and 'show mock footer' options.

Disabled UI elements

The disabledUI property allows you to specify which UI elements should be disabled in the widget. Disabling UI elements can be useful to prevent user interaction with certain parts of the widget that might not be necessary or desirable for your specific implementation.

The DisabledUI enum specifies UI elements that can be disabled to prevent user interaction.

export enum DisabledUI {
  // Disables the input field for the token amount
  FromAmount = 'fromAmount',
  // Disables the button for the source token selection
  FromToken = 'fromToken',
  // Disables the button for specifying the destination address
  ToAddress = 'toAddress',
  // Disables the button for the destination token selection
  ToToken = 'toToken',
}

Hidden UI elements

The hiddenUI property allows you to specify which UI elements should be hidden in the widget. This is useful for tailoring the user interface to fit your specific needs by removing elements that might not be relevant for your use case.

The HiddenUI enum specifies UI elements that can be hidden from the UI.

export enum HiddenUI {
  // Hides the appearance settings UI (light/dark mode switch)
  Appearance = 'appearance',
  // Hides the close button in the drawer variant
  DrawerCloseButton = 'drawerCloseButton',
  // Hides the transaction history UI
  History = 'history',
  // Hides the language selection UI
  Language = 'language',
  // Hides the "Powered by LI.FI" branding - not recommended :)
  PoweredBy = 'poweredBy',
  // Hides the button for specifying the destination address
  ToAddress = 'toAddress',
  // Hides the button for the destination token selection
  ToToken = 'toToken',
  // Hides the wallet menu UI
  WalletMenu = 'walletMenu',
}

The following example shows how to hide appearance and language settings in the UI.

import { LiFiWidget, WidgetConfig } from '@lifi/widget';
import { useMemo } from 'react';

export const WidgetPage = () => {
  const widgetConfig: WidgetConfig = useMemo(() => ({
      hiddenUI: ['language', 'appearance'],
    }), []);

  return (
    <LiFiWidget integrator="Your dApp/company name" config={widgetConfig} />
  );
};

Required UI elements

The requiredUI property allows you to specify which UI elements should be required in the widget. This means that the user must interact with these elements for the widget to proceed with swapping/bridging. This is useful for ensuring that certain critical inputs are provided by the user.

The RequiredUI enum specifies UI elements that are required to be filled out or interacted with by the user.

export enum RequiredUI {
  // Makes the button for the destination address required for interaction
  ToAddress = 'toAddress',
}

Required destination address

Making the destination address required can come in handy when developers want to build a flow where only a pre-configured list of wallet addresses can be set as the destination. See Configure a curated list of wallet addresses for more details.

If you are interested in additional customization options for your service, reach out via our Discord or Partnership page.

As you can see, widget customization is pretty straightforward. We are eager to see what combinations you will come up with as we continue to add new customization options.

Widget Events

Last updated