diff --git a/.changeset/README.md b/.changeset/README.md
index c2668e7..5aa11a9 100644
--- a/.changeset/README.md
+++ b/.changeset/README.md
@@ -8,17 +8,19 @@ We have a quick list of common questions to get you started engaging with this p
 [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
 
 # Usage
+
 1. `npx changeset` to create a changeset
 
-2. Select change type: 
+2. Select change type:
+
 ```
-Major: 
+Major:
 - Describes when to use a major change, focusing on breaking changes.
 
-Minor: 
+Minor:
 - Describes when to use a minor change, focusing on new features that are backward compatible.
 
-Patch: 
+Patch:
 - Describes when to use a patch change, focusing on bug fixes and small improvements.
 ```
 
@@ -31,5 +33,6 @@ Patch:
 9. After reviewing, merge the `Version Packages` PR on Github, the package version is then updated and published to npm.
 
 Notes:
+
 - The `Version Packages` is automatically updated with the latest changeset(s).
 - The `Version Packages` will only update the package versions and publish to npm, not the actual code.
diff --git a/.changeset/weak-drinks-punch.md b/.changeset/weak-drinks-punch.md
new file mode 100644
index 0000000..6275ca6
--- /dev/null
+++ b/.changeset/weak-drinks-punch.md
@@ -0,0 +1,5 @@
+---
+"@babylonlabs-io/bbn-core-ui": minor
+---
+
+add table component
diff --git a/README.md b/README.md
index b852e48..aa6f915 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ npm run storybook
 Provide examples of how to use the library in a project. Include code snippets and explanations.
 
 ```javascript
-import { ComponentName } from '@babylonlabs-io/bbn-core-ui';
+import { ComponentName } from "@babylonlabs-io/bbn-core-ui";
 
 function App() {
   return <ComponentName prop="value" />;
diff --git a/package-lock.json b/package-lock.json
index 520c12c..5da55f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "@babylonlabs-io/bbn-core-ui",
-  "version": "0.0.7",
+  "version": "0.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@babylonlabs-io/bbn-core-ui",
-      "version": "0.0.7",
+      "version": "0.2.0",
       "devDependencies": {
         "@changesets/cli": "^2.27.9",
         "@chromatic-com/storybook": "^3.2.2",
diff --git a/public/images/fps/lombard.jpeg b/public/images/fps/lombard.jpeg
new file mode 100644
index 0000000..b529dce
Binary files /dev/null and b/public/images/fps/lombard.jpeg differ
diff --git a/public/images/fps/pumpbtc.jpeg b/public/images/fps/pumpbtc.jpeg
new file mode 100644
index 0000000..dd0356a
Binary files /dev/null and b/public/images/fps/pumpbtc.jpeg differ
diff --git a/public/images/fps/solv.jpeg b/public/images/fps/solv.jpeg
new file mode 100644
index 0000000..e01360a
Binary files /dev/null and b/public/images/fps/solv.jpeg differ
diff --git a/src/components/Accordion/Accordion.css b/src/components/Accordion/Accordion.css
index 2977212..9e42d9c 100644
--- a/src/components/Accordion/Accordion.css
+++ b/src/components/Accordion/Accordion.css
@@ -2,7 +2,7 @@
   @apply transition-opacity;
 
   &-summary {
-    @apply cursor-pointer relative pr-5 transition-colors;
+    @apply relative cursor-pointer pr-5 transition-colors;
   }
 
   &-details {
diff --git a/src/components/Dialog/index.css b/src/components/Dialog/index.css
index d9ab95f..d3f3a21 100644
--- a/src/components/Dialog/index.css
+++ b/src/components/Dialog/index.css
@@ -24,4 +24,4 @@
   &-mobile {
     @apply fixed inset-x-0 bottom-0 z-50 flex flex-col rounded-t-3xl bg-[#ffffff] px-4 pb-4 pt-6;
   }
-}
\ No newline at end of file
+}
diff --git a/src/components/Table/Table.css b/src/components/Table/Table.css
new file mode 100644
index 0000000..48ca32c
--- /dev/null
+++ b/src/components/Table/Table.css
@@ -0,0 +1,189 @@
+/* Table wrapper styles - controls scrolling behavior */
+.bbn-table-wrapper {
+  @apply relative h-full w-full overflow-auto;
+
+  /* Hide scrollbar for Firefox */
+  scrollbar-width: none;
+  /* Hide scrollbar for IE and Edge */
+  -ms-overflow-style: none;
+
+  /* Hide WebKit scrollbar */
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+
+/* Main table styles */
+.bbn-table {
+  @apply w-full border-separate border-spacing-0;
+
+  /* Header row styles */
+  &-header {
+    @apply sticky top-0 z-30 bg-secondary-contrast text-sm font-medium text-primary-light transition-shadow;
+    
+    tr {
+      @apply relative;
+    }
+
+    /* Header cell styles */
+    th {
+      @apply whitespace-nowrap px-6 py-3 transition-all border-b border-[#f9f9f9];
+      width: var(--column-width);
+      min-width: var(--column-width);
+      max-width: var(--column-width);
+
+      &:first-child {
+        @apply border-l;
+      }
+
+      &:last-child {
+        @apply border-r;
+      }
+    }
+
+    /* First header cell when fixed */
+    th:first-child.bbn-table-fixed {
+      @apply sticky left-0 z-30 bg-secondary-contrast;
+      width: fit-content;
+      min-width: max-content;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+    
+    &.scrolled-top {
+      @apply shadow-[0_2px_4px_rgba(0,0,0,0.05)];
+    }
+  }
+
+  /* Table body styles */
+  &-body {
+    /* Row styles */
+    tr {
+      @apply transition-colors;
+
+      td {
+        @apply border-b border-t border-[#f9f9f9];
+
+        &:first-child {
+          @apply border-l;
+        }
+
+        &:last-child {
+          @apply border-r;
+        }
+      }
+
+      &:hover td {
+        @apply bg-primary-contrast;
+      }
+
+      &.selected td {
+        @apply bg-primary-contrast border-secondary-main;
+      }
+    }
+
+    /* Odd row styles */
+    tr:nth-child(odd) {
+      @apply bg-[#F9F9F9];
+
+      &:hover {
+        @apply bg-primary-contrast;
+      }
+
+      td.bbn-table-cell-hover {
+        @apply bg-primary-contrast/50;
+      }
+
+      /* First cell when fixed in odd rows */
+      td:first-child.bbn-table-fixed {
+        @apply sticky left-0 z-20 bg-[#F9F9F9];
+        width: fit-content;
+        min-width: max-content;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+
+    /* Even row styles */
+    tr:nth-child(even) {
+      @apply bg-secondary-contrast;
+
+      &:hover {
+        @apply bg-primary-contrast;
+      }
+
+      td.bbn-table-cell-hover {
+        @apply bg-primary-contrast/50;
+      }
+
+      /* First cell when fixed in even rows */
+      td:first-child.bbn-table-fixed {
+        @apply sticky left-0 z-20 bg-secondary-contrast;
+        width: fit-content;
+        min-width: max-content;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+
+    /* Standard cell styles */
+    td {
+      @apply px-6 py-4 text-sm text-primary-light transition-colors;
+      width: var(--column-width);
+      min-width: var(--column-width);
+      max-width: var(--column-width);
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+
+  /* Sortable column styles */
+  &-sortable {
+    @apply cursor-pointer select-none;
+  }
+
+  /* Sort icons container */
+  &-sort-icons {
+    @apply ml-1 inline-flex flex-col;
+
+    svg {
+      @apply h-4 w-4;
+    }
+
+    /* Individual sort icon styles */
+    .bbn-sort-icon {
+      @apply text-primary/20 transition-colors;
+
+      &.bbn-sort-icon-up {
+        @apply -mb-1;
+      }
+
+      &.bbn-sort-icon-down {
+        @apply -mt-1;
+      }
+
+      &.bbn-sort-icon-active {
+        @apply !text-primary-light;
+      }
+
+      &.bbn-sort-icon-inactive {
+        @apply !text-primary/10;
+      }
+    }
+  }
+
+  /* Cell alignment styles */
+  &-cell {
+    &-right {
+      @apply text-right;
+    }
+
+    &-left {
+      @apply text-left;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/Table/Table.stories.tsx b/src/components/Table/Table.stories.tsx
new file mode 100644
index 0000000..d9355d1
--- /dev/null
+++ b/src/components/Table/Table.stories.tsx
@@ -0,0 +1,121 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import { useState } from "react";
+
+import { Table } from "./";
+import { Avatar } from "../Avatar";
+
+const meta: Meta<typeof Table> = {
+  component: Table,
+  tags: ["autodocs"],
+};
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+interface FinalityProvider {
+  id: string;
+  name: string;
+  icon: string;
+  status: string;
+  btcPk: string;
+  totalDelegation: number;
+  commission: number;
+}
+
+const data: FinalityProvider[] = [
+  {
+    id: "1",
+    name: "Lombard",
+    icon: "/images/fps/lombard.jpeg",
+    status: "Active",
+    btcPk: "1234...4321",
+    totalDelegation: 10,
+    commission: 1,
+  },
+  {
+    id: "2",
+    name: "Solv Protocol",
+    icon: "/images/fps/solv.jpeg",
+    status: "Active",
+    btcPk: "1234...4321",
+    totalDelegation: 20,
+    commission: 3,
+  },
+  {
+    id: "3",
+    name: "PumpBTC",
+    icon: "/images/fps/pumpbtc.jpeg",
+    status: "Active",
+    btcPk: "1234...4321",
+    totalDelegation: 30,
+    commission: 5,
+  },
+];
+
+export const Default: Story = {
+  render: () => {
+    const [tableData, setTableData] = useState(data.slice(0, 3));
+    const [loading, setLoading] = useState(false);
+    const [hasMore, setHasMore] = useState(true);
+
+    const handleLoadMore = async () => {
+      setLoading(true);
+      await new Promise((resolve) => setTimeout(resolve, 1000));
+
+      const nextItems = data.slice(tableData.length, tableData.length + 3);
+      setTableData((prev) => [...prev, ...nextItems]);
+      setHasMore(tableData.length + nextItems.length < data.length);
+      setLoading(false);
+    };
+
+    const handleRowSelect = (row: FinalityProvider) => {
+      console.log(row);
+    };
+
+    return (
+      <div className="h-[150px]">
+        <Table
+          data={tableData}
+          hasMore={hasMore}
+          loading={loading}
+          onLoadMore={handleLoadMore}
+          onRowSelect={handleRowSelect}
+          columns={[
+            {
+              key: "name",
+              header: "Finality Provider",
+              render: (_, row) => (
+                <div className="flex items-center gap-2">
+                  <Avatar size="small" url={row.icon} alt={row.name} />
+                  <span className="text-primary-light">{row.name}</span>
+                </div>
+              ),
+              sorter: (a, b) => a.name.localeCompare(b.name),
+            },
+            {
+              key: "status",
+              header: "Status",
+            },
+            {
+              key: "btcPk",
+              header: "BTC PK",
+            },
+            {
+              key: "totalDelegation",
+              header: "Total Delegation",
+              render: (value) => `${value} sBTC`,
+              sorter: (a, b) => a.totalDelegation - b.totalDelegation,
+            },
+            {
+              key: "commission",
+              header: "Commission",
+              render: (value) => `${value}%`,
+              sorter: (a, b) => a.commission - b.commission,
+            },
+          ]}
+        />
+      </div>
+    );
+  },
+};
diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx
new file mode 100644
index 0000000..2e9882c
--- /dev/null
+++ b/src/components/Table/Table.tsx
@@ -0,0 +1,148 @@
+import { useRef, useMemo, useState } from "react";
+import { twJoin } from "tailwind-merge";
+import { useTableScroll } from "@/hooks/useTableScroll";
+import { TableContext, TableContextType } from "../../context/Table.context";
+import { Column } from "./components/Column";
+import { Cell } from "./components/Cell";
+import type { TableProps } from "./types";
+import "./Table.css";
+
+export function Table<T extends { id: string | number }>({
+  data,
+  columns,
+  className,
+  hasMore = false,
+  loading = false,
+  onLoadMore,
+  onRowSelect,
+  ...restProps
+}: TableProps<T>) {
+  const tableRef = useRef<HTMLDivElement>(null);
+  const [hoveredColumn, setHoveredColumn] = useState<string | undefined>(undefined);
+  const [sortStates, setSortStates] = useState<{
+    [key: string]: { direction: "asc" | "desc" | null; priority: number };
+  }>({});
+  const [selectedRow, setSelectedRow] = useState<string | number | undefined>(undefined);
+
+  const { isScrolledTop } = useTableScroll(tableRef, {
+    onLoadMore,
+    hasMore,
+    loading,
+  });
+
+  const handleHoveredColumn = (column: string) => {
+    if (hoveredColumn === column) return;
+    setHoveredColumn(column);
+  };
+
+  const handleRowSelect = (row: T) => {
+    if (selectedRow === row.id) return;
+    setSelectedRow(row.id);
+    onRowSelect?.(row);
+  };
+
+  const handleColumnSort = (columnKey: string, sorter?: (a: T, b: T) => number) => {
+    if (!sorter) return;
+
+    setSortStates((prev) => {
+      const currentState = prev[columnKey]?.direction ?? null;
+      const currentPriority = prev[columnKey]?.priority ?? 0;
+
+      const nextDirection: "asc" | "desc" | null =
+        currentState === null ? "asc" : currentState === "asc" ? "desc" : null;
+
+      if (nextDirection === null) {
+        const newState = { ...prev };
+        delete newState[columnKey];
+
+        for (const key in newState) {
+          if (newState[key].priority > currentPriority) {
+            newState[key].priority--;
+          }
+        }
+        return newState;
+      }
+
+      const highestPriority = Math.max(0, ...Object.values(prev).map((s) => s.priority));
+      return {
+        ...prev,
+        [columnKey]: {
+          direction: nextDirection,
+          priority: highestPriority + 1,
+        },
+      };
+    });
+  };
+
+  const sortedData = useMemo(() => {
+    const activeSorters = Object.entries(sortStates)
+      .filter(([, state]) => state.direction !== null)
+      .sort((a, b) => b[1].priority - a[1].priority)
+      .map(([key, state]) => ({
+        column: columns.find((col) => col.key === key),
+        direction: state.direction,
+      }))
+      .filter(({ column }) => column?.sorter);
+
+    if (activeSorters.length === 0) return data;
+
+    return [...data].sort((a, b) => {
+      for (const { column, direction } of activeSorters) {
+        const result = column!.sorter!(a, b);
+        if (result !== 0) {
+          return direction === "asc" ? result : -result;
+        }
+      }
+      return 0;
+    });
+  }, [data, columns, sortStates]);
+
+  const contextValue = useMemo(
+    () => ({
+      data: sortedData,
+      columns,
+      sortStates,
+      hoveredColumn,
+      onColumnHover: handleHoveredColumn,
+      onColumnSort: handleColumnSort,
+      onRowSelect: handleRowSelect,
+    }),
+    [sortedData, columns, sortStates, hoveredColumn, handleHoveredColumn, handleColumnSort, handleRowSelect],
+  );
+
+  return (
+    <TableContext.Provider value={contextValue as TableContextType<unknown>}>
+      <div ref={tableRef} className="bbn-table-wrapper">
+        <table className={twJoin("bbn-table", className)} {...restProps}>
+          <thead className={twJoin("bbn-table-header", isScrolledTop && "scrolled-top")}>
+            <tr>
+              {columns.map((column) => (
+                <Column key={column.key} name={column.key} sorter={column.sorter}>
+                  {column.header}
+                </Column>
+              ))}
+            </tr>
+          </thead>
+          <tbody className="bbn-table-body">
+            {sortedData.map((row) => (
+              <tr
+                key={row.id}
+                className={twJoin(selectedRow === row.id && "selected", "cursor-pointer")}
+                onClick={() => handleRowSelect(row)}
+              >
+                {columns.map((column) => (
+                  <Cell
+                    key={column.key}
+                    value={row[column.key as keyof T]}
+                    columnName={column.key}
+                    render={column.render ? (value) => column.render!(value, row) : undefined}
+                  />
+                ))}
+              </tr>
+            ))}
+          </tbody>
+        </table>
+      </div>
+    </TableContext.Provider>
+  );
+}
diff --git a/src/components/Table/components/Cell.tsx b/src/components/Table/components/Cell.tsx
new file mode 100644
index 0000000..ad57302
--- /dev/null
+++ b/src/components/Table/components/Cell.tsx
@@ -0,0 +1,30 @@
+import { type PropsWithChildren, type HTMLAttributes, useContext, type ReactNode } from "react";
+import { twJoin } from "tailwind-merge";
+import { TableContext } from "../../../context/Table.context";
+
+interface CellProps {
+  className?: string;
+  render?: (value: unknown) => ReactNode;
+  columnName?: string;
+  value: unknown;
+}
+
+export function Cell({
+  className,
+  render,
+  value,
+  columnName,
+  ...restProps
+}: PropsWithChildren<CellProps & HTMLAttributes<HTMLTableCellElement>>) {
+  const { hoveredColumn } = useContext(TableContext);
+
+  return (
+    <td
+      className={twJoin(`bbn-cell-left`, columnName === hoveredColumn && "bbn-table-cell-hover", className)}
+      data-column={columnName}
+      {...restProps}
+    >
+      {render ? render(value) : (value as ReactNode)}
+    </td>
+  );
+}
diff --git a/src/components/Table/components/Column.tsx b/src/components/Table/components/Column.tsx
new file mode 100644
index 0000000..5805c3a
--- /dev/null
+++ b/src/components/Table/components/Column.tsx
@@ -0,0 +1,58 @@
+import { type PropsWithChildren, type HTMLAttributes, useContext } from "react";
+import { twJoin } from "tailwind-merge";
+import { RiArrowUpSFill, RiArrowDownSFill } from "react-icons/ri";
+import { TableContext } from "../../../context/Table.context";
+
+interface ColumnProps<T = unknown> {
+  name?: string;
+  sorter?: (a: T, b: T) => number;
+  className?: string;
+}
+
+export function Column<T>({
+  name,
+  className,
+  children,
+  sorter,
+  ...restProps
+}: PropsWithChildren<ColumnProps<T> & HTMLAttributes<HTMLTableCellElement>>) {
+  const { columns, sortStates, onColumnSort, onColumnHover } = useContext(TableContext);
+  const sortState = sortStates[name ?? ""];
+  const sortDirection = sortState?.direction;
+
+  return (
+    <th
+      className={twJoin(`bbn-cell-left`, sorter && "bbn-table-sortable", className)}
+      onClick={() => {
+        if (sorter && name) {
+          const column = columns.find((col) => col.key === name);
+          onColumnSort?.(name, column?.sorter);
+        }
+      }}
+      onMouseEnter={() => name && onColumnHover?.(name)}
+      onMouseLeave={() => onColumnHover?.(undefined)}
+      data-column={name}
+      {...restProps}
+    >
+      <div className="flex items-center justify-between gap-1">
+        <span>{children}</span>
+        {sorter && (
+          <span className="bbn-table-sort-icons">
+            <RiArrowUpSFill
+              className={twJoin(
+                "bbn-sort-icon bbn-sort-icon-up",
+                sortDirection === "asc" ? "bbn-sort-icon-active" : "bbn-sort-icon-inactive",
+              )}
+            />
+            <RiArrowDownSFill
+              className={twJoin(
+                "bbn-sort-icon bbn-sort-icon-down",
+                sortDirection === "desc" ? "bbn-sort-icon-active" : "bbn-sort-icon-inactive",
+              )}
+            />
+          </span>
+        )}
+      </div>
+    </th>
+  );
+}
diff --git a/src/components/Table/index.ts b/src/components/Table/index.ts
new file mode 100644
index 0000000..d69eb77
--- /dev/null
+++ b/src/components/Table/index.ts
@@ -0,0 +1,4 @@
+export * from "./Table";
+export * from "./types";
+export * from "./components/Cell";
+export * from "./components/Column";
diff --git a/src/components/Table/types/index.ts b/src/components/Table/types/index.ts
new file mode 100644
index 0000000..127f9bd
--- /dev/null
+++ b/src/components/Table/types/index.ts
@@ -0,0 +1,18 @@
+import type { ReactNode } from "react";
+
+export type ColumnProps<T = unknown> = {
+  key: string;
+  header: string;
+  render?: (value: unknown, row: T) => ReactNode;
+  sorter?: (a: T, b: T) => number;
+};
+
+export interface TableProps<T extends { id: string | number }> {
+  data: T[];
+  columns: ColumnProps<T>[];
+  className?: string;
+  hasMore?: boolean;
+  loading?: boolean;
+  onLoadMore?: () => void;
+  onRowSelect?: (row: T) => void;
+}
diff --git a/src/context/Table.context.tsx b/src/context/Table.context.tsx
new file mode 100644
index 0000000..f9818d6
--- /dev/null
+++ b/src/context/Table.context.tsx
@@ -0,0 +1,27 @@
+import { createContext } from "react";
+import type { ColumnProps } from "../components/Table/types";
+
+export interface TableContextType<T = unknown> {
+  data: T[];
+  columns: ColumnProps<T>[];
+  sortStates: {
+    [key: string]: {
+      direction: "asc" | "desc" | null;
+      priority: number;
+    };
+  };
+  hoveredColumn?: string;
+  onColumnHover?: (column: string | undefined) => void;
+  onColumnSort?: (columnKey: string, sorter?: (a: T, b: T) => number) => void;
+  onRowSelect?: (row: T) => void;
+}
+
+export const TableContext = createContext<TableContextType<unknown>>({
+  data: [],
+  columns: [],
+  sortStates: {},
+  hoveredColumn: undefined,
+  onColumnHover: undefined,
+  onColumnSort: undefined,
+  onRowSelect: undefined,
+});
diff --git a/src/hooks/useControlledState.ts b/src/hooks/useControlledState.ts
index 8af30e8..bfbe88e 100644
--- a/src/hooks/useControlledState.ts
+++ b/src/hooks/useControlledState.ts
@@ -6,7 +6,11 @@ interface Options<V> {
   onStateChange?: (state: V) => void;
 }
 
-export function useControlledState<V>({ value: controlledState, defaultValue: defaultState, onStateChange }: Options<V> = {}): [V | undefined, (state: V) => void] {
+export function useControlledState<V>({
+  value: controlledState,
+  defaultValue: defaultState,
+  onStateChange,
+}: Options<V> = {}): [V | undefined, (state: V) => void] {
   const [uncontrolledState, setUncontrolledState] = useState(defaultState);
   const { current: isControlled } = useRef(controlledState != null);
 
@@ -20,7 +24,7 @@ export function useControlledState<V>({ value: controlledState, defaultValue: de
 
       onStateChange?.(newValue);
     },
-    [isControlled, onStateChange, setUncontrolledState]
+    [isControlled, onStateChange, setUncontrolledState],
   );
 
   return [state, handleStateChange];
diff --git a/src/hooks/useTableScroll.ts b/src/hooks/useTableScroll.ts
new file mode 100644
index 0000000..1ef6ea4
--- /dev/null
+++ b/src/hooks/useTableScroll.ts
@@ -0,0 +1,35 @@
+import { RefObject, useEffect, useState } from "react";
+
+interface UseTableScrollOptions {
+  onLoadMore?: () => void;
+  hasMore?: boolean;
+  loading?: boolean;
+}
+
+export function useTableScroll(
+  tableRef: RefObject<HTMLDivElement>,
+  { onLoadMore, hasMore = false, loading = false }: UseTableScrollOptions = {},
+) {
+  const [isScrolledTop, setIsScrolledTop] = useState(false);
+
+  useEffect(() => {
+    const handleScroll = (e: Event) => {
+      const target = e.target as HTMLDivElement;
+      setIsScrolledTop(target.scrollTop > 0);
+
+      if (!loading && hasMore && target.scrollHeight - target.scrollTop <= target.clientHeight + 100) {
+        onLoadMore?.();
+      }
+    };
+
+    const tableWrapper = tableRef.current;
+    if (tableWrapper) {
+      tableWrapper.addEventListener("scroll", handleScroll);
+      return () => tableWrapper.removeEventListener("scroll", handleScroll);
+    }
+  }, [loading, hasMore, onLoadMore, tableRef]);
+
+  return {
+    isScrolledTop,
+  };
+}
diff --git a/src/index.css b/src/index.css
index eeba232..40c76b2 100644
--- a/src/index.css
+++ b/src/index.css
@@ -26,4 +26,4 @@
   .custom-scrollbar::-webkit-scrollbar-track {
     @apply bg-primary;
   }
-}
\ No newline at end of file
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
index fc713ae..07fb3ab 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -29,7 +29,7 @@
     "paths": {
       "@/*": ["*", "./*"]
     },
-    "outDir": "dist",
+    "outDir": "dist"
   },
   "include": ["src"],
   "keywords": ["react", "ui-components", "babylonlabs", "bitcoin", "bitcoin-ui"]