DEV Community

Cover image for How to integrate LiFi Widget on your NextJS app
Elisha Okon
Elisha Okon

Posted on

How to integrate LiFi Widget on your NextJS app

Imagine seamlessly integrating the power of LI.FI, a cutting-edge multi-chain liquidity aggregation protocol, is added to your Next.js application. LI.FI facilitates any-to-any swaps by consolidating bridges and decentralized exchange (DEX) aggregators across over 20 networks. In this article, we'll explore the process of integrating the LI.FI protocol into your Next.js app, unlocking a world of possibilities for frictionless cryptocurrency trading and enhanced liquidity management.

Introduction

Introducing LiFi:
LI.FI is a multi-chain liquidity aggregation protocol that supports any-2-any swaps by aggregating bridges and DEX aggregators across +20 networks. You can see it as a super-intelligent system that gathers all the places where people exchange money, like bringing together all the shops in a big marketplace. It helps people trade their money from one type to another, making it easy to switch between different currencies. So, instead of going to many other shops, LiFi brings them all to one place, making it simple and convenient for people to exchange money.

LiFi Widget:
The LI.FI Widget comprises ready-made user interface elements seamlessly incorporating a secure cross-chain bridging and swapping feature into your web application. These components can be customized to align perfectly with your app's design, enhancing its visual appeal. By integrating the LI.FI Widget, you can bolster your multi-chain strategy and draw in users from various locations, providing a user-friendly and secure experience.

Let’s build our app together

Setting up your Next.js app:
You can begin setting up your NextJS application using these guidelines:

Installation: Start by installing Next.js using npm or yarn. Run the following command in your terminal:

npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode

Project Structure: Next.js follows a convention-based file structure. Familiarize yourself with the default project structure, including pages, public, and style directories.
Here's how your folder structure would look like after installation of NextJS:

Project Structure

We have successfully set up NextJS for our integration.

Installing libraries
Installing LIFI Libraries:
We have to install LiFi libraries in our NextJS app to access its functionality in our web app. Use npm or yarn to install the LI.FI Widget library. Run the following command in your terminal:

npm install --save @lifi/widget
Enter fullscreen mode Exit fullscreen mode

Or for those who use Yarn,

yarn add @lifi/widget
Enter fullscreen mode Exit fullscreen mode

This command will install the LIFI Widget package as a dependency on your project.

Package.json file showing lifi library

By installing the LI.FI Widget library, you'll gain access to pre-built UI components and functionality that enable secure cross-chain bridging and swapping within your Next.js application. This installation process sets the stage for integrating LI.FI's powerful features seamlessly into your project.

Installing the Widget
Integrating the widget
Create a new folder named components in your /app directory. After which we'll create our widget component named Widget.tsx and paste this code there:

'use client';

import type { WidgetConfig } from '@lifi/widget';
import { LiFiWidget, WidgetSkeleton } from '@lifi/widget';
import { ClientOnly } from './ClientOnly';

export function Widget() {
  const config = {
    appearance: 'light',
    theme: {
      container: {
        boxShadow: '0px 8px 32px rgba(0, 0, 0, 0.08)',
        borderRadius: '16px',
      },
    },
  } as Partial<WidgetConfig>;

  return (
    <main>
      <ClientOnly fallback={<WidgetSkeleton config={config} />}>
        <LiFiWidget config={config} integrator="nextjs-example" />
      </ClientOnly>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

I am sure you're wondering why we have other components imported into our Widget component now; here is why: in modern web development, ensuring a smooth user experience is paramount. The ClientOnly Component serves a crucial role by selectively rendering content only after client-side JavaScript has fully loaded. This prevents users from encountering incomplete or broken UI elements during the loading process, thereby enhancing the application's overall usability and professionalism.

So, on that note, let's create a new component named ClientOnly.tsx

import { type PropsWithChildren } from 'react';
import { useHydrated } from '../hooks/useHydrated';

interface ClientOnlyProps extends PropsWithChildren {
  fallback?: React.ReactNode;
}

/**
 * Render the children only after the JS has loaded client-side. Use an optional
 * fallback component if the JS is not yet loaded.
 */
export function ClientOnly({ children, fallback = null }: ClientOnlyProps) {
  const hydrated = useHydrated();
  return hydrated ? children : fallback;
}

Enter fullscreen mode Exit fullscreen mode

We also have the WidgetEvents.tsx component that lets listeners track key events in our widget's lifecycle, like route executions and value changes. It keeps our widget responsive and finely tuned, providing real-time insights into user interactions for a seamless experience.
Let's create our WidgetEvents.tsx component

'use client';
import type { Route } from '@lifi/sdk';
import type {
  RouteExecutionUpdate,
  RouteHighValueLossUpdate,
} from '@lifi/widget';
import { WidgetEvent, useWidgetEvents } from '@lifi/widget';
import { useEffect } from 'react';

export const WidgetEvents = () => {
  const widgetEvents = useWidgetEvents();

  useEffect(() => {
    const onRouteExecutionStarted = (route: Route) => {
      console.log('onRouteExecutionStarted fired.');
    };
    const onRouteExecutionUpdated = (update: RouteExecutionUpdate) => {
      console.log('onRouteExecutionUpdated fired.');
    };
    const onRouteExecutionCompleted = (route: Route) => {
      console.log('onRouteExecutionCompleted fired.');
    };
    const onRouteExecutionFailed = (update: RouteExecutionUpdate) => {
      console.log('onRouteExecutionFailed fired.');
    };
    const onRouteHighValueLoss = (update: RouteHighValueLossUpdate) => {
      console.log('onRouteHighValueLoss continued.');
    };
    widgetEvents.on(WidgetEvent.RouteExecutionStarted, onRouteExecutionStarted);
    widgetEvents.on(WidgetEvent.RouteExecutionUpdated, onRouteExecutionUpdated);
    widgetEvents.on(
      WidgetEvent.RouteExecutionCompleted,
      onRouteExecutionCompleted,
    );
    widgetEvents.on(WidgetEvent.RouteHighValueLoss, onRouteHighValueLoss);
    widgetEvents.on(WidgetEvent.RouteExecutionFailed, onRouteExecutionFailed);
    return () => widgetEvents.all.clear();
  }, [widgetEvents]);

  return null;
};
Enter fullscreen mode Exit fullscreen mode

Let's do something. How do we check if Javascript has finished loading on the client side of our app? Let's write code that does that.

In your /app directory, create a new folder named hooks, and inside it, a file named useHydrated.tsx and paste the following code into it

import { useSyncExternalStore } from 'react';

function subscribe() {
  return () => {};
}

/**
 * Return a boolean indicating if the JS has been hydrated already.
 * When doing Server-Side Rendering, the result will always be false.
 * When doing Client-Side Rendering, the result will always be false on the
 * first render and true from then on. Even if a new component renders it will
 * always start with true.
 */
export function useHydrated() {
  return useSyncExternalStore(
    subscribe,
    () => true,
    () => false,
  );
}

Enter fullscreen mode Exit fullscreen mode

This code lets us check if the JavaScript has finished loading on the client side in our React app. It uses a hook called useHydrated. When you use it, it'll return false during server-side rendering, but once the JavaScript is loaded on the client, it'll switch to true and stay that way. It's a quick way to know if your app is ready to rock on the client side.

You might want to grab a glass of water if you have reached this point. If you did this correctly, your folder structure should look like this:

Showing folder structure after setting up our app

Let's edit our page.tsx file in the /app Directory. Replace the existing code there with this:

import { Widget } from './components/Widget';
import { WidgetEvents } from './components/WidgetEvents';

export default function Home() {
  return (
    <main>
      <WidgetEvents />
      <Widget />
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Open your terminal again and use this command to run your server.

cd lifi && yarn dev
Enter fullscreen mode Exit fullscreen mode

From my own terminal, it seems like everything is running smooth

CLI showing the host url

Now head on to your browser and visit localhost:3000

Our local hosted web page displaying lifi widget

Conclusion

Now, we have our fully functional widget fully integrated in our NextJS App. Do you have any questions or come across any bugs while following this article? Reach out to me on:

Linkedin: Elisha David

X: Defidevrel

Top comments (0)