title: Infinite Grid
description: A high-performance, interactive 3D infinite grid component built with OGL as seen on phantom.land
---

```vue
<template>
  <ClientOnly>
    <div class="flex flex-col h-[28rem] w-full items-center justify-center relative">
      <div
        ref="el"
        class="absolute inset-0 z-50 flex flex-col justify-center items-center"
        :class="{}"
      >
        <InfiniteGrid
          :card-data
          @tiles-loaded="onTilesLoaded"
        />
      </div>
      <span
        v-if="!tilesLoaded"
        class="text-lg font-semibold z-[51]"
      >
        Loading Tiles
      </span>

      <InteractiveHoverButton
        v-if="tilesLoaded"
        text="View Fullscreen"
        class="absolute bottom-0 z-[52]"
        @click="toggle"
      />
    </div>
  </ClientOnly>
</template>

<script setup lang="ts">
import { useFullscreen } from "@vueuse/core";
import { useTemplateRef } from "vue";

const el = useTemplateRef<HTMLElement>("el");

const { toggle } = useFullscreen(el);
const tilesLoaded = ref(false);

const cardData = [
  {
    title: "Visionary Insights",
    badge: "New",
    image: "https://picsum.photos/1366/768",
    description: "Next-gen AI system delivering business intelligence in real-time",
    tags: ["AI", "Big Data", "BI"],
    date: "2025",
  },
  {
    title: "UI Framework",
    badge: "Updated",
    image: "https://picsum.photos/1366/768",
    description: "Robust design framework featuring sleek and responsive components",
    tags: ["UI", "React", "Toolkit"],
    date: "2025",
  },
  {
    title: "NextGen App",
    badge: "Beta",
    image: "https://picsum.photos/1366/768",
    description: "Progressive mobile-first app optimized for speed and usability",
    tags: ["Mobile", "Flutter", "UX"],
    date: "2025",
  },
  {
    title: "Edge Gateway",
    badge: "Stable",
    image: "https://picsum.photos/1366/768",
    description: "Smart API gateway enabling distributed edge routing",
    tags: ["API", "Edge", "Infrastructure"],
    date: "2025",
  },
  {
    title: "Insight Panel",
    badge: "Live",
    image: "https://picsum.photos/1366/768",
    description: "Customizable analytics panel for performance monitoring",
    tags: ["Dashboard", "Vue", "Insights"],
    date: "2025",
  },
  {
    title: "DataCore",
    badge: "Optimized",
    image: "https://picsum.photos/1366/768",
    description: "Reliable database solution with advanced indexing",
    tags: ["SQL", "Database", "Indexing"],
    date: "2025",
  },
  {
    title: "Fortress Auth",
    badge: "Critical",
    image: "https://picsum.photos/1366/768",
    description: "Advanced identity platform with biometric authentication",
    tags: ["Security", "OAuth2", "Biometrics"],
    date: "2024",
  },
  {
    title: "OpsHub",
    badge: "Automated",
    image: "https://picsum.photos/1366/768",
    description: "Infrastructure as code with seamless deployment pipelines",
    tags: ["DevOps", "Terraform", "CI/CD"],
    date: "2024",
  },
  {
    title: "TestBench",
    badge: "Complete",
    image: "https://picsum.photos/1366/768",
    description: "Unified testing platform supporting unit and E2E tests",
    tags: ["QA", "Testing", "Mocha"],
    date: "2024",
  },
  {
    title: "AI Metrics",
    badge: "New",
    image: "https://picsum.photos/1366/768",
    description: "Cloud-native platform analyzing ML model performance",
    tags: ["ML", "AI", "Monitoring"],
    date: "2025",
  },
  {
    title: "Component Forge",
    badge: "Updated",
    image: "https://picsum.photos/1366/768",
    description: "Reusable UI components with seamless design integration",
    tags: ["React", "UI Kit", "Figma"],
    date: "2025",
  },
  {
    title: "Hybrid Mobile",
    badge: "Beta",
    image: "https://picsum.photos/1366/768",
    description: "Unified codebase for building apps across all platforms",
    tags: ["Ionic", "Mobile", "Cross-platform"],
    date: "2025",
  },
  {
    title: "Micro API Hub",
    badge: "Stable",
    image: "https://picsum.photos/1366/768",
    description: "Lightweight microservices API router for distributed systems",
    tags: ["Microservices", "API", "Express"],
    date: "2025",
  },
  {
    title: "Live Metrics",
    badge: "Live",
    image: "https://picsum.photos/1366/768",
    description: "Streaming dashboard with real-time data visualization",
    tags: ["Monitoring", "Charts", "D3.js"],
    date: "2025",
  },
  {
    title: "DataVault",
    badge: "Optimized",
    image: "https://picsum.photos/1366/768",
    description: "Secure and scalable data storage with automated backups",
    tags: ["PostgreSQL", "Cloud", "Storage"],
    date: "2025",
  },
  {
    title: "ShieldWall",
    badge: "Critical",
    image: "https://picsum.photos/1366/768",
    description: "Enterprise-grade security with threat detection engine",
    tags: ["Security", "Firewall", "Zero Trust"],
    date: "2024",
  },
  {
    title: "Pipeline Master",
    badge: "Automated",
    image: "https://picsum.photos/1366/768",
    description: "Containerized deployment pipeline with monitoring hooks",
    tags: ["CI/CD", "Kubernetes", "Docker"],
    date: "2024",
  },
  {
    title: "AssureTest",
    badge: "Complete",
    image: "https://picsum.photos/1366/768",
    description: "End-to-end test framework with smart coverage analysis",
    tags: ["Cypress", "QA", "Automation"],
    date: "2024",
  },
];

function onTilesLoaded() {
  tilesLoaded.value = true;
}
</script>

```

::alert{type="info"}
**Note:** This component uses OGL & requires `ogl` and `gsap` as dependencies.
::

## API

| Prop Name  | Type                           | Default | Description                                                                   |
| ---------- | ------------------------------ | ------- | ----------------------------------------------------------------------------- |
| `cardData` | `CardData[]`                   | `[]`    | Data for every tile shown in the grid. **Required**.                          |
| `options`  | `Partial<InfiniteGridOptions>` | `{}`    | Optional overrides for layout, camera, and post-processing (see table below). |

### `InfiniteGridOptions`

| Option                                  | Type      | Default | Description                                         |
| --------------------------------------- | --------- | ------- | --------------------------------------------------- |
| `gridCols`                              | `number`  | `4`     | Grid Columns .                                      |
| `gridRows`                              | `number`  | `4`     | Grid Rows .                                         |
| `gridGap`                               | `number`  | `0`     | Gap between squares.                                |
| `tileSize`                              | `number`  | `2.4`   | Tile size (OGL units).                              |
| `baseCameraZ`                           | `number`  | `10`    | Starting Z-distance of the camera.                  |
| `enablePostProcessing`                  | `boolean` | `true`  | Toggle the post-processing pipeline.                |
| `postProcessParams.distortionIntensity` | `number`  | `-0.2`  | Barrel / pincushion distortion strength (0 = none). |
| `postProcessParams.vignetteOffset`      | `number`  | `0.0`   | Vignette offset; higher ⇒ smaller clear area.       |
| `postProcessParams.vignetteDarkness`    | `number`  | `0.0`   | Vignette darkness; higher ⇒ darker edges.           |

---

### `CardData`

| Field         | Type       | Required | Description                                   |
| ------------- | ---------- | -------- | --------------------------------------------- |
| `title`       | `string`   | ✓        | Main heading text.                            |
| `badge`       | `string`   | ✓        | Badge label (render logic can be customised). |
| `description` | `string`   | –        | Longer body text.                             |
| `tags`        | `string[]` | ✓        | Tag pills rendered at the bottom.             |
| `date`        | `string`   | ✓        | Date string shown bottom-right.               |
| `image`       | `string`   | –        | URL for the tile's background image.          |

---

## Emits

| Event Name     | Payload                             | Description                                                                                                             |
| -------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `tileClicked`  | `{ card: CardData, index: number }` | Fired whenever a tile is clicked/tapped. The payload contains the clicked `CardData` object and its zero-based `index`. |
| `onTileLoaded` | -                                   | Fired when all the images are loaded inside the tile.                                                                   |

## Install using CLI

```vue
<InstallationCli component-id="infinite-grid" />
```

## Install Manually

#### Install the dependencies

::code-group

```bash [npm]
npm install ogl gsap
```

```bash [pnpm]
pnpm install ogl gsap
```

```bash [bun]
bun add ogl gsap
```

```bash [yarn]
yarn add ogl gsap
```

::

#### Copy and paste the following code in the same folder

::code-group

:CodeViewerTab{label="InfiniteGrid.vue" language="vue" componentName="InfiniteGrid" type="ui" id="infinite-grid"}

:CodeViewerTab{filename="InfiniteGridClass.ts" language="typescript" componentName="InfiniteGridClass" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="DisposalManager.ts" language="typescript" componentName="DisposalManager" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="EventHandler.ts" language="typescript" componentName="EventHandler" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="GridManager.ts" language="typescript" componentName="GridManager" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="PostProcessShader.ts" language="typescript" componentName="PostProcessShader" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="createTexture.ts" language="typescript" componentName="createTexture" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="shaders.ts" language="typescript" componentName="shaders" type="ui" id="infinite-grid" extension="ts"}
:CodeViewerTab{filename="types.ts" language="typescript" componentName="types" type="ui" id="infinite-grid" extension="ts"}

::

## Credits

- Inspired from [Phantom Land](https://phantom.land)
- Special thanks to [Safak Dinc](https://github.com/safakdinc) for the idea and for granting permission to include it here. You can find the original repository at [infinite-grid](https://github.com/safakdinc/infinite-grid). Your contribution is greatly appreciated!
- Credits to [kalix127](https://github.com/kalix127) for porting this component.