Skip to content
islandcn
Esc
↑↓navigate↵open⌘Jpreview
On this page

Installation

Add the islandcn engine to a React project with the shadcn CLI and mount the island once.

By the end of this page your app renders a black pill at the top centre of the window, ready for activities.

You need React 19 and a project set up for shadcn (a components.json at the root). If you don’t have one, run npx shadcn@latest init first.

Install the engine

npx shadcn@latest add https://islandcn.dev/r/island-core.json
pnpm dlx shadcn@latest add https://islandcn.dev/r/island-core.json
yarn dlx shadcn@latest add https://islandcn.dev/r/island-core.json
bunx shadcn@latest add https://islandcn.dev/r/island-core.json

This writes the engine to lib/island/, under your lib alias. It adds no npm packages.

Mount the provider and the island

Render IslandProvider once, high in the tree, and Island once inside it. placement="viewport" pins the pill to the top centre of the window. Without it, the pill draws at the top of its nearest positioned parent, which is what you want inside a phone mockup.

IslandProvider holds state, so it renders on the client. Put it in a client file and use that file from the root layout.

"use client";

import type { ReactNode } from "react";

import { Island, IslandProvider } from "@/lib/island";

export const Providers = ({ children }: { children: ReactNode }) => (
  <IslandProvider>
    <Island placement="viewport" />
    {children}
  </IslandProvider>
);
import type { ReactNode } from "react";

import { Providers } from "./providers";

const RootLayout = ({ children }: { children: ReactNode }) => (
  <html lang="en">
    <body>
      <Providers>{children}</Providers>
    </body>
  </html>
);

export default RootLayout;

Wrap the root once in main.tsx. The shadcn CLI needs the @ path alias in tsconfig.json and vite.config.ts; npx shadcn@latest init tells you if it is missing.

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import { Island, IslandProvider } from "@/lib/island";

import { App } from "./app";

const root = document.getElementById("root");

if (root) {
  createRoot(root).render(
    <StrictMode>
      <IslandProvider>
        <Island placement="viewport" />
        <App />
      </IslandProvider>
    </StrictMode>,
  );
}

Check it

Reload the page. A black pill sits at the top centre. It does nothing yet: it waits for an activity.

Add a preset

Each preset is its own registry item and names it on its page. The first one you add also installs island-content, the kit the presets share. For example, the timer:

npx shadcn@latest add https://islandcn.dev/r/timer-activity.json
pnpm dlx shadcn@latest add https://islandcn.dev/r/timer-activity.json
yarn dlx shadcn@latest add https://islandcn.dev/r/timer-activity.json
bunx shadcn@latest add https://islandcn.dev/r/timer-activity.json

Done when

  • lib/island/index.ts exists.
  • The page shows a black pill at the top centre when no activity runs.
  • Starting an activity grows the pill, and ending it returns the pill to rest. The quick start does exactly that.

Was this page helpful?