diff --git a/src/client.ts b/src/client.ts
index 8fb3e10..b127d9a 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -4,3 +4,5 @@ export * from "./primitives/MarkdownEditor";
 export * from "./components/Form";
 export * from "./components/GenericProgressForm";
 export * from "./components/MultipleSelect";
+export * from "./features/pool/components/PoolList";
+export * from "./features/program/components/ProgramList";
diff --git a/src/components/GenericProgressForm/GenericProgressForm.stories.tsx b/src/components/GenericProgressForm/GenericProgressForm.stories.tsx
index 6592754..2afd9c4 100644
--- a/src/components/GenericProgressForm/GenericProgressForm.stories.tsx
+++ b/src/components/GenericProgressForm/GenericProgressForm.stories.tsx
@@ -1,8 +1,11 @@
+import { action } from "@storybook/addon-actions";
 import type { Meta, StoryObj } from "@storybook/react";
 
 import { GenericProgressForm } from "./GenericProgressForm";
 import { roundSetupSteps } from "./mocks/RoundSetup";
 
+const onSubmit = action("onSubmit");
+
 const meta: Meta<typeof GenericProgressForm> = {
   title: "Components/GenericProgressForm",
   component: GenericProgressForm,
@@ -16,9 +19,7 @@ export const Default: Story = {
   args: {
     name: "Round setup",
     steps: roundSetupSteps,
-    onSubmit: async (values: any) => {
-      console.log("Submitted final values:", values);
-    },
+    onSubmit: async (values: any) => onSubmit(values),
     dbName: "formDB",
     storeName: "formDrafts",
   },
diff --git a/src/features/pool/components/PoolCard/PoolCard.stories.tsx b/src/features/pool/components/PoolCard/PoolCard.stories.tsx
index 62d65d3..563fbe9 100644
--- a/src/features/pool/components/PoolCard/PoolCard.stories.tsx
+++ b/src/features/pool/components/PoolCard/PoolCard.stories.tsx
@@ -6,7 +6,7 @@ import { PoolStatus, PoolType } from "@/types";
 
 import { PoolCard, PoolCardProps, PoolCardQueryProps } from "./PoolCard";
 
-const onProgramClick = action("Pool Clicked!");
+const onPoolClick = action("Pool Clicked!");
 
 const simpleRound = {
   roundName: "Grants Round Defi",
@@ -21,7 +21,8 @@ const simpleRound = {
   operatorsCount: 10,
   logoImg:
     "https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
-  onClick: () => onProgramClick,
+  onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
+  createdAtBlock: 123456,
 };
 
 export default {
@@ -39,6 +40,8 @@ export default {
     votingEndDate: { control: "date" },
     operatorsCount: { control: "number" },
     queryResult: { table: { disable: true } }, // Hide queryResult from controls
+    createdAtBlock: { control: "number" },
+    onClick: { action: "onClick" },
   },
 } as Meta<typeof PoolCard>;
 
diff --git a/src/features/pool/components/PoolCard/PoolDataCard.tsx b/src/features/pool/components/PoolCard/PoolDataCard.tsx
index fabba0e..1c0c5a5 100644
--- a/src/features/pool/components/PoolCard/PoolDataCard.tsx
+++ b/src/features/pool/components/PoolCard/PoolDataCard.tsx
@@ -15,8 +15,13 @@ export function PoolDataCard({ data }: PoolDataCardProps) {
   const { name, icon } = getChainInfo(data.chainId);
   return (
     <div
-      onClick={data.onClick}
-      className="inline-flex h-60 w-full items-center justify-between rounded-2xl border border-grey-100 p-6"
+      onClick={() =>
+        data.onClick?.({
+          chainId: data.chainId,
+          roundId: data.roundId,
+        })
+      }
+      className="inline-flex h-60 w-full cursor-pointer items-center justify-between rounded-2xl border border-grey-100 p-6"
     >
       <div className="flex items-center justify-start gap-6">
         <img className="relative size-48 rounded-2xl" src={data.logoImg} />
diff --git a/src/features/pool/components/PoolCardGroup/PoolCardGroup.stories.tsx b/src/features/pool/components/PoolCardGroup/PoolCardGroup.stories.tsx
index dda5e12..669e0e6 100644
--- a/src/features/pool/components/PoolCardGroup/PoolCardGroup.stories.tsx
+++ b/src/features/pool/components/PoolCardGroup/PoolCardGroup.stories.tsx
@@ -5,7 +5,7 @@ import { PoolStatus, PoolType } from "@/types";
 
 import { PoolCardGroup } from "./PoolCardGroup";
 
-const onProgramClick = action("Pool Clicked!");
+const onPoolClick = action("Pool Clicked!");
 
 const pools = [
   {
@@ -19,9 +19,10 @@ const pools = [
     votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
     votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
     operatorsCount: 10,
+    createdAtBlock: 1234567890,
     logoImg:
       "https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
-    onClick: () => onProgramClick,
+    onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
   },
   {
     roundName: "Uniswap",
@@ -35,7 +36,8 @@ const pools = [
     votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
     operatorsCount: 5,
     logoImg: "https://thegivingblock.com/wp-content/uploads/2021/07/Uniswap-Logo.png",
-    onClick: () => onProgramClick,
+    createdAtBlock: 1234567890,
+    onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
   },
 ];
 
diff --git a/src/features/pool/components/PoolCardGroup/PoolCardGroup.tsx b/src/features/pool/components/PoolCardGroup/PoolCardGroup.tsx
index 57451a4..99e56b1 100644
--- a/src/features/pool/components/PoolCardGroup/PoolCardGroup.tsx
+++ b/src/features/pool/components/PoolCardGroup/PoolCardGroup.tsx
@@ -39,8 +39,8 @@ const justifyVariants = tv({
 export const PoolCardGroup = ({ pools, justify }: PoolCardGroupProps) => {
   return (
     <div className={justifyVariants({ justify })}>
-      {pools.map((stat, index) => (
-        <PoolCard key={index} {...stat} />
+      {pools.map((pool, index) => (
+        <PoolCard key={index} {...pool} />
       ))}
     </div>
   );
diff --git a/src/features/pool/components/PoolList/PoolList.stories.tsx b/src/features/pool/components/PoolList/PoolList.stories.tsx
new file mode 100644
index 0000000..9d86d61
--- /dev/null
+++ b/src/features/pool/components/PoolList/PoolList.stories.tsx
@@ -0,0 +1,67 @@
+import { action } from "@storybook/addon-actions";
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { PoolType } from "@/types";
+import { PoolStatus } from "@/types/pool";
+
+import { PoolList } from "./PoolList";
+
+const onPoolClick = action("Pool clicked!");
+
+const meta: Meta<typeof PoolList> = {
+  title: "Features/Pool/PoolList",
+  component: PoolList,
+};
+
+export default meta;
+type Story = StoryObj<typeof PoolList>;
+
+const mockPools = [
+  {
+    roundName: "Grants Round Defi",
+    roundId: "90",
+    chainId: 10,
+    poolType: PoolType.QuadraticFunding,
+    poolStatus: PoolStatus.ApplicationsInProgress,
+    applicationStartDate: new Date("2024-12-08T19:22:56.413Z"),
+    applicationEndDate: new Date("2024-12-09T19:23:30.678Z"),
+    votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
+    votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
+    operatorsCount: 10,
+    createdAtBlock: 100000,
+    logoImg:
+      "https://cdn.prod.website-files.com/6433c5d029c6bb75f3f00bd5/66f47dd26d8ec8d0e48a22d0_gitcoin-profile.png",
+  },
+  {
+    roundName: "Uniswap",
+    roundId: "91",
+    chainId: 8453,
+    poolType: PoolType.DirectGrants,
+    poolStatus: PoolStatus.FundingPending,
+    applicationStartDate: new Date("2024-12-08T19:22:56.413Z"),
+    applicationEndDate: new Date("2024-12-09T19:23:30.678Z"),
+    votingStartDate: new Date("2024-12-09T19:22:56.413Z"),
+    votingEndDate: new Date("2024-12-10T19:23:30.678Z"),
+    operatorsCount: 5,
+    createdAtBlock: 1000,
+    logoImg: "https://thegivingblock.com/wp-content/uploads/2021/07/Uniswap-Logo.png",
+  },
+];
+
+export const Default: Story = {
+  args: {
+    pools: mockPools.map((pool) => ({
+      ...pool,
+      onClick: (pool?: { chainId: number; roundId: string }) => onPoolClick(pool),
+    })),
+    title: "Available Pools",
+    noPoolsPlaceholder: "No pools found",
+  },
+};
+
+export const Empty: Story = {
+  args: {
+    ...Default.args,
+    pools: [],
+  },
+};
diff --git a/src/features/pool/components/PoolList/PoolList.tsx b/src/features/pool/components/PoolList/PoolList.tsx
new file mode 100644
index 0000000..be4a0e4
--- /dev/null
+++ b/src/features/pool/components/PoolList/PoolList.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import { useState } from "react";
+
+import { MultipleSelect } from "@/components/MultipleSelect";
+
+import { PoolCardProps } from "../PoolCard";
+import { PoolCardGroup } from "../PoolCardGroup";
+import { useFilteredAndOrderedPools } from "./hooks/useFilteredAndOrderedPools";
+import { getSortFilterOptions } from "./utils";
+
+export interface PoolListProps {
+  pools: PoolCardProps[];
+  title?: string;
+  noPoolsPlaceholder?: string;
+}
+
+export const PoolList = ({
+  pools,
+  title = "Pools",
+  noPoolsPlaceholder = "No pools found",
+}: PoolListProps) => {
+  const [order, setOrder] = useState<Record<string, string[]>>({});
+  const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({});
+
+  const { orderOptions, filterOptions } = getSortFilterOptions(pools);
+
+  const handleFilterChange = (values: Record<string, string[]>) => {
+    setSelectedFilters(values);
+  };
+
+  const handleOrderChange = (values: Record<string, string[]>) => {
+    setOrder(values);
+  };
+
+  const filteredAndOrderedPools = useFilteredAndOrderedPools({ pools, order, selectedFilters });
+
+  return (
+    <div className="flex w-full flex-col gap-4">
+      <div className="flex w-full items-center justify-between">
+        <div className="flex-1 text-start font-ui-sans text-2xl font-medium">
+          {`${title} (${pools.length})`}
+        </div>
+        <div className="flex items-center gap-2">
+          <div className="flex items-center gap-2">
+            <div className="text-nowrap font-ui-sans text-body font-medium">Order by</div>
+            <MultipleSelect
+              options={orderOptions}
+              onChange={handleOrderChange}
+              defaultValue={{ "ORDER BY TIME": ["Recent"] }}
+              className="w-40"
+              variants={{ triggerTextColor: "green", itemsPosition: "end", headerPosition: "end" }}
+            />
+          </div>
+          <div className="flex items-center gap-2">
+            <div className="text-nowrap font-ui-sans text-body font-medium">Filter by</div>
+            <MultipleSelect
+              options={filterOptions}
+              onChange={handleFilterChange}
+              defaultValue={{ ungrouped: ["All"] }} // so it starts with 'All' selected
+              className="w-64"
+              variants={{ triggerTextColor: "red" }}
+            />
+          </div>
+        </div>
+      </div>
+      <div className="w-full">
+        {filteredAndOrderedPools.length > 0 ? (
+          <PoolCardGroup pools={filteredAndOrderedPools} />
+        ) : (
+          <div className="font-ui-sans text-lg">{noPoolsPlaceholder}</div>
+        )}
+      </div>
+    </div>
+  );
+};
diff --git a/src/features/pool/components/PoolList/hooks/index.ts b/src/features/pool/components/PoolList/hooks/index.ts
new file mode 100644
index 0000000..5a7e0c1
--- /dev/null
+++ b/src/features/pool/components/PoolList/hooks/index.ts
@@ -0,0 +1 @@
+export * from "./useFilteredAndOrderedPools";
diff --git a/src/features/pool/components/PoolList/hooks/useFilteredAndOrderedPools.ts b/src/features/pool/components/PoolList/hooks/useFilteredAndOrderedPools.ts
new file mode 100644
index 0000000..e0380a5
--- /dev/null
+++ b/src/features/pool/components/PoolList/hooks/useFilteredAndOrderedPools.ts
@@ -0,0 +1,70 @@
+import { useMemo } from "react";
+
+import { PoolData, PoolStatus } from "@/types/pool";
+
+interface UseFilteredAndOrderedPoolsProps {
+  pools: PoolData[];
+  selectedFilters: Record<string, string[]>;
+  order: Record<string, string[]>;
+}
+
+export const useFilteredAndOrderedPools = ({
+  pools,
+  selectedFilters,
+  order,
+}: UseFilteredAndOrderedPoolsProps) => {
+  return useMemo(() => {
+    let result = [...pools];
+
+    // 1) Check for "All" filter
+    const ungrouped = selectedFilters["ungrouped"];
+    const hasAllFilter = ungrouped?.includes("All");
+
+    // 2) Filter by network
+    const selectedNetworks = selectedFilters["Network"] || [];
+    if (!hasAllFilter && selectedNetworks.length > 0) {
+      result = result.filter((pool) => selectedNetworks.includes(pool.chainId.toString()));
+    }
+
+    // 3) Filter by status
+    const selectedStatuses = selectedFilters["Status"] || [];
+    if (!hasAllFilter && selectedStatuses.length > 0) {
+      result = result.filter((pool) => {
+        return selectedStatuses.some((status) => {
+          switch (status) {
+            case "active":
+              return (
+                pool.poolStatus === PoolStatus.RoundInProgress ||
+                pool.poolStatus === PoolStatus.ApplicationsInProgress
+              );
+            case "applications":
+              return pool.poolStatus === PoolStatus.ApplicationsInProgress;
+            case "finished":
+              return pool.poolStatus === PoolStatus.FundingPending;
+            default:
+              return false;
+          }
+        });
+      });
+    }
+
+    // 4) Apply ordering
+    const orderValue = order["ORDER BY TIME"]?.[0] || order["ORDER BY NAME"]?.[0] || "Recent";
+    switch (orderValue) {
+      case "Recent":
+        result.sort((a, b) => b.createdAtBlock - a.createdAtBlock);
+        break;
+      case "Oldest":
+        result.sort((a, b) => a.createdAtBlock - b.createdAtBlock);
+        break;
+      case "A-Z":
+        result.sort((a, b) => a.roundName.localeCompare(b.roundName));
+        break;
+      case "Z-A":
+        result.sort((a, b) => b.roundName.localeCompare(a.roundName));
+        break;
+    }
+
+    return result;
+  }, [pools, order, selectedFilters]);
+};
diff --git a/src/features/pool/components/PoolList/index.ts b/src/features/pool/components/PoolList/index.ts
new file mode 100644
index 0000000..c8dbaa9
--- /dev/null
+++ b/src/features/pool/components/PoolList/index.ts
@@ -0,0 +1,3 @@
+export * from "./PoolList";
+export * from "./utils";
+export * from "./hooks";
diff --git a/src/features/pool/components/PoolList/utils.ts b/src/features/pool/components/PoolList/utils.ts
new file mode 100644
index 0000000..e344da3
--- /dev/null
+++ b/src/features/pool/components/PoolList/utils.ts
@@ -0,0 +1,76 @@
+import { MultipleSelectGroup } from "@/components/MultipleSelect/types";
+import { getChainInfo } from "@/lib";
+import { PoolData } from "@/types/pool";
+
+export const getSortFilterOptions = (pools: PoolData[]) => {
+  const orderOptions = [
+    {
+      groupLabel: "ORDER BY TIME",
+      multiple: false,
+      items: ["Recent", "Oldest"].map((value) => ({
+        label: value,
+        value,
+        exclusive: true,
+        exclusiveScope: "global",
+      })),
+    },
+    {
+      groupLabel: "ORDER BY NAME",
+      multiple: false,
+      items: ["A-Z", "Z-A"].map((value) => ({
+        label: value,
+        value,
+        exclusive: true,
+        exclusiveScope: "global",
+      })),
+    },
+  ] satisfies MultipleSelectGroup[];
+
+  // Example "All" ungrouped + networks + statuses
+  const filterOptions = [
+    {
+      multiple: false,
+      items: [
+        {
+          label: "All",
+          value: "All",
+          exclusive: true,
+          exclusiveScope: "global",
+        },
+      ],
+    },
+    {
+      groupLabel: "Network",
+      multiple: true,
+      collapsible: true,
+      items: [...new Set(pools.map((pool) => pool.chainId))].map((chainId) => {
+        const chainInfo = getChainInfo(chainId);
+        return {
+          label: `Rounds on ${chainInfo.name}`,
+          value: chainId.toString(),
+        };
+      }),
+    },
+    {
+      groupLabel: "Status",
+      multiple: true,
+      collapsible: true,
+      items: [
+        {
+          label: "Active",
+          value: "active",
+        },
+        {
+          label: "Taking Applications",
+          value: "applications",
+        },
+        {
+          label: "Finished",
+          value: "finished",
+        },
+      ],
+    },
+  ] satisfies MultipleSelectGroup[];
+
+  return { orderOptions, filterOptions };
+};
diff --git a/src/features/pool/index.ts b/src/features/pool/index.ts
index 0eedc81..84fdd0d 100644
--- a/src/features/pool/index.ts
+++ b/src/features/pool/index.ts
@@ -1,6 +1,7 @@
 export * from "./components/PoolBadge";
 export * from "./components/PoolCard";
 export * from "./components/PoolCardGroup";
+export * from "./components/PoolList";
 export * from "./components/PoolStatusBadge";
 export * from "./components/PoolSummary";
 export * from "./components/PoolTypeBadge";
diff --git a/src/features/program/components/ProgramCard/ProgramCard.stories.tsx b/src/features/program/components/ProgramCard/ProgramCard.stories.tsx
index d5345c6..64da8cf 100644
--- a/src/features/program/components/ProgramCard/ProgramCard.stories.tsx
+++ b/src/features/program/components/ProgramCard/ProgramCard.stories.tsx
@@ -13,7 +13,8 @@ const program: ProgramCardProps = {
   title: "Gitcoin Grants Stack",
   operatorsCount: 2,
   roundsCount: 10,
-  onClick: () => onProgramClick(),
+  createdAtBlock: 1000000,
+  onClick: (program?: { chainId: number; programId: string }) => onProgramClick(program),
 };
 
 export default {
diff --git a/src/features/program/components/ProgramCard/ProgramCard.tsx b/src/features/program/components/ProgramCard/ProgramCard.tsx
index b2d5613..af19f1d 100644
--- a/src/features/program/components/ProgramCard/ProgramCard.tsx
+++ b/src/features/program/components/ProgramCard/ProgramCard.tsx
@@ -16,7 +16,8 @@ export interface ProgramCardProps {
   title: string;
   operatorsCount: number;
   roundsCount: number;
-  onClick?: () => void;
+  createdAtBlock: number;
+  onClick?: (program?: { chainId: number; programId: string }) => void;
 }
 export interface ProgramCardQueryProps {
   queryResult: UseQueryResult<ProgramCardProps, Error>;
@@ -42,8 +43,8 @@ export function ProgramDataCard({ data }: ProgramDataCardProps) {
   const { name, icon } = getChainInfo(data.chainId);
   return (
     <Card
-      className="block w-[304px] overflow-hidden border-grey-300 bg-grey-50"
-      onClick={data.onClick}
+      className="block w-[304px] cursor-pointer overflow-hidden border-grey-300 bg-grey-50"
+      onClick={() => data.onClick?.({ chainId: data.chainId, programId: data.id })}
     >
       <CardContent className="flex flex-col gap-3 p-6">
         <h2 className="truncate font-ui-sans text-2xl font-bold">{data.title}</h2>
diff --git a/src/features/program/components/ProgramCardGroup/ProgramCardGroup.stories.tsx b/src/features/program/components/ProgramCardGroup/ProgramCardGroup.stories.tsx
index dd283d7..4c8273a 100644
--- a/src/features/program/components/ProgramCardGroup/ProgramCardGroup.stories.tsx
+++ b/src/features/program/components/ProgramCardGroup/ProgramCardGroup.stories.tsx
@@ -12,7 +12,8 @@ const programs = [
     title: "Gitcoin Grants Stack",
     operatorsCount: 2,
     roundsCount: 10,
-    onClick: () => onProgramClick(),
+    createdAtBlock: 1000,
+    onClick: (program?: { chainId: number; programId: string }) => onProgramClick(program),
   },
   {
     id: "0x3456",
@@ -20,7 +21,8 @@ const programs = [
     title: "Allo Protocol",
     operatorsCount: 4,
     roundsCount: 2,
-    onClick: () => onProgramClick(),
+    createdAtBlock: 1000000,
+    onClick: (program?: { chainId: number; programId: string }) => onProgramClick(program),
   },
 ];
 
@@ -96,6 +98,7 @@ export const withFourCard: Story = {
         title: "Pump Fun",
         operatorsCount: 4,
         roundsCount: 2,
+        createdAtBlock: 1000000,
         onClick: () => onProgramClick(),
       },
     ],
@@ -112,6 +115,7 @@ export const withFiveCard: Story = {
         title: "Pump Fun",
         operatorsCount: 4,
         roundsCount: 2,
+        createdAtBlock: 1000000,
         onClick: () => onProgramClick(),
       },
       {
@@ -120,6 +124,7 @@ export const withFiveCard: Story = {
         title: "Eigen Protocol",
         operatorsCount: 4,
         roundsCount: 2,
+        createdAtBlock: 1000000,
         onClick: () => onProgramClick(),
       },
     ],
diff --git a/src/features/program/components/ProgramList/ProgramList.stories.tsx b/src/features/program/components/ProgramList/ProgramList.stories.tsx
new file mode 100644
index 0000000..cf7701d
--- /dev/null
+++ b/src/features/program/components/ProgramList/ProgramList.stories.tsx
@@ -0,0 +1,51 @@
+import { action } from "@storybook/addon-actions";
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { ProgramList } from "./ProgramList";
+
+const onProgramClick = action("Program clicked!");
+
+const meta: Meta<typeof ProgramList> = {
+  title: "Features/Program/ProgramList",
+  component: ProgramList,
+};
+
+export default meta;
+type Story = StoryObj<typeof ProgramList>;
+
+const mockPrograms = [
+  {
+    id: "0x123456789",
+    chainId: 1,
+    title: "Gitcoin Grants Stack",
+    operatorsCount: 2,
+    roundsCount: 10,
+    createdAtBlock: 100000000,
+  },
+  {
+    id: "0x3456",
+    chainId: 10,
+    title: "Allo Protocol",
+    operatorsCount: 4,
+    roundsCount: 2,
+    createdAtBlock: 1000000,
+  },
+];
+
+export const Default: Story = {
+  args: {
+    programs: mockPrograms.map((program) => ({
+      ...program,
+      onClick: (program?: { programId: string; chainId: number }) => onProgramClick(program),
+    })),
+    title: "Available Programs",
+    noProgramsPlaceholder: "No programs found",
+  },
+};
+
+export const Empty: Story = {
+  args: {
+    ...Default.args,
+    programs: [],
+  },
+};
diff --git a/src/features/program/components/ProgramList/ProgramList.tsx b/src/features/program/components/ProgramList/ProgramList.tsx
new file mode 100644
index 0000000..d741bf1
--- /dev/null
+++ b/src/features/program/components/ProgramList/ProgramList.tsx
@@ -0,0 +1,82 @@
+"use client";
+
+import { useState } from "react";
+
+import { MultipleSelect } from "@/components/MultipleSelect";
+
+import { ProgramCardProps } from "../ProgramCard";
+import { ProgramCardGroup } from "../ProgramCardGroup";
+import { useFilteredAndOrderedPrograms } from "./hooks/useFilteredAndOrderedPrograms";
+import { getOrderAndFilterOptions } from "./utils";
+
+export interface ProgramListProps {
+  programs: ProgramCardProps[];
+  title?: string;
+  noProgramsPlaceholder: string;
+}
+
+export const ProgramList = ({
+  programs,
+  noProgramsPlaceholder = "No programs found",
+  title = "Programs",
+}: ProgramListProps) => {
+  const [order, setOrder] = useState<Record<string, string[]>>({ "ORDER BY TIME": ["Recent"] });
+  const [selectedFilters, setSelectedFilters] = useState<Record<string, string[]>>({
+    ungrouped: ["All"],
+  });
+
+  const { orderOptions, filterOptions } = getOrderAndFilterOptions(programs);
+
+  const handleOrderChange = (values: Record<string, string[]>) => {
+    setOrder(values);
+  };
+
+  const handleFilterChange = (values: Record<string, string[]>) => {
+    setSelectedFilters(values);
+  };
+
+  const filteredAndOrderedPrograms = useFilteredAndOrderedPrograms({
+    programs,
+    selectedFilters,
+    order,
+  });
+
+  return (
+    <div className="flex flex-col gap-4">
+      <div className="flex items-center justify-between">
+        <div className="flex font-ui-sans text-2xl font-medium">{`${title} (${programs.length})`}</div>
+        <div className="flex gap-2">
+          <div className="flex items-center justify-center gap-2">
+            <span className="text-nowrap font-ui-sans text-body font-medium">Order by</span>
+            <MultipleSelect
+              options={orderOptions}
+              onChange={handleOrderChange}
+              defaultValue={{ "ORDER BY TIME": ["Recent"] }}
+              placeholder="Select order"
+              variants={{ triggerTextColor: "green", headerPosition: "end", itemsPosition: "end" }}
+              className="w-40"
+            />
+          </div>
+          <div className="flex items-center gap-2">
+            <div className="text-nowrap font-ui-sans text-body font-medium">Filter by</div>
+            <MultipleSelect
+              options={filterOptions}
+              onChange={handleFilterChange}
+              defaultValue={{ ungrouped: ["All"] }}
+              placeholder="Select filters"
+              variants={{ triggerTextColor: "red" }}
+              className="w-64"
+            />
+          </div>
+        </div>
+      </div>
+      <div className="w-full">
+        {filteredAndOrderedPrograms.length > 0 ? (
+          <ProgramCardGroup programs={filteredAndOrderedPrograms} />
+        ) : (
+          <div className="font-ui-sans text-lg">{noProgramsPlaceholder}</div>
+        )}
+      </div>
+    </div>
+  );
+};
diff --git a/src/features/program/components/ProgramList/hooks/useFilteredAndOrderedPrograms.ts b/src/features/program/components/ProgramList/hooks/useFilteredAndOrderedPrograms.ts
new file mode 100644
index 0000000..e329575
--- /dev/null
+++ b/src/features/program/components/ProgramList/hooks/useFilteredAndOrderedPrograms.ts
@@ -0,0 +1,48 @@
+import { useMemo } from "react";
+
+import { ProgramCardProps } from "@/features/program";
+
+interface UseFilteredAndOrderedProgramsProps {
+  programs: ProgramCardProps[];
+  selectedFilters: Record<string, string[]>;
+  order: Record<string, string[]>;
+}
+
+export const useFilteredAndOrderedPrograms = ({
+  programs,
+  selectedFilters,
+  order,
+}: UseFilteredAndOrderedProgramsProps) => {
+  return useMemo(() => {
+    let result = [...programs];
+
+    // Apply filters
+    const ungrouped = selectedFilters["ungrouped"];
+    const hasAllFilter = ungrouped?.includes("All");
+
+    // Apply network filters if not "All"
+    const selectedNetworks = selectedFilters["Network"] || [];
+    if (!hasAllFilter && selectedNetworks.length > 0) {
+      result = result.filter((program) => selectedNetworks.includes(program.chainId.toString()));
+    }
+
+    // Apply ordering
+    const orderValue = order["ORDER BY TIME"]?.[0] || order["ORDER BY NAME"]?.[0] || "Recent";
+    switch (orderValue) {
+      case "Recent":
+        result.sort((a, b) => b.createdAtBlock - a.createdAtBlock);
+        break;
+      case "Oldest":
+        result.sort((a, b) => a.createdAtBlock - b.createdAtBlock);
+        break;
+      case "A-Z":
+        result.sort((a, b) => a.title.localeCompare(b.title));
+        break;
+      case "Z-A":
+        result.sort((a, b) => b.title.localeCompare(a.title));
+        break;
+    }
+
+    return result;
+  }, [programs, order, selectedFilters]);
+};
diff --git a/src/features/program/components/ProgramList/index.ts b/src/features/program/components/ProgramList/index.ts
new file mode 100644
index 0000000..57aa0dd
--- /dev/null
+++ b/src/features/program/components/ProgramList/index.ts
@@ -0,0 +1 @@
+export * from "./ProgramList";
diff --git a/src/features/program/components/ProgramList/utils.ts b/src/features/program/components/ProgramList/utils.ts
new file mode 100644
index 0000000..51442b2
--- /dev/null
+++ b/src/features/program/components/ProgramList/utils.ts
@@ -0,0 +1,53 @@
+import { MultipleSelectGroup } from "@/components/MultipleSelect";
+import { ProgramCardProps } from "@/features/program";
+import { getChainInfo } from "@/lib";
+
+export const getOrderAndFilterOptions = (programs: ProgramCardProps[]) => {
+  const orderOptions = [
+    {
+      groupLabel: "ORDER BY TIME",
+      multiple: false,
+      items: ["Recent", "Oldest"].map((value) => ({
+        label: value,
+        value,
+        exclusive: true,
+        exclusiveScope: "global",
+      })),
+    },
+    {
+      groupLabel: "ORDER BY NAME",
+      multiple: false,
+      items: ["A-Z", "Z-A"].map((value) => ({
+        label: value,
+        value,
+        exclusive: true,
+        exclusiveScope: "global",
+      })),
+    },
+  ] satisfies MultipleSelectGroup[];
+
+  const filterOptions = [
+    {
+      multiple: false,
+      items: [
+        {
+          label: "All",
+          value: "All",
+          exclusive: true,
+          exclusiveScope: "global",
+        },
+      ],
+    },
+    {
+      groupLabel: "Network",
+      multiple: true,
+      collapsible: true,
+      items: [...new Set(programs.map((program) => program.chainId))].map((chainId) => ({
+        label: `Rounds on ${getChainInfo(chainId).name}`,
+        value: chainId.toString(),
+      })),
+    },
+  ] satisfies MultipleSelectGroup[];
+
+  return { orderOptions, filterOptions };
+};
diff --git a/src/features/program/index.ts b/src/features/program/index.ts
index 4720f4f..f5c9a4d 100644
--- a/src/features/program/index.ts
+++ b/src/features/program/index.ts
@@ -1,2 +1,3 @@
 export * from "./components/ProgramCard";
 export * from "./components/ProgramCardGroup";
+export * from "./components/ProgramList";
diff --git a/src/types/pool.ts b/src/types/pool.ts
index e8d3a7e..593a048 100644
--- a/src/types/pool.ts
+++ b/src/types/pool.ts
@@ -30,8 +30,9 @@ export interface PoolData {
   votingEndDate: Date;
   poolStatus: PoolStatus;
   operatorsCount: number;
+  createdAtBlock: number;
   logoImg?: string;
-  onClick?: () => void;
+  onClick?: (pool?: { chainId: number; roundId: string }) => void;
 }
 
 // Type guard for PoolData