How to Remove Orphaned/Unused Compositions

Last updated: June 26, 2026

Problem Statement

A project can accumulate compositions that are no longer attached to any project map node. Unless they are referenced directly in code, these orphaned compositions are safe to delete — but the composition search only filters by fields and types, so finding them takes a different approach.

Solution

1. Find orphaned compositions in the UI

When you create a new project map node, select the option to see existing compositions — compositions not yet attached to a node are listed there. Note their names, then locate and delete them from the composition list.

image.png

2. Remove them in bulk via the API (for long lists)

  1. Get a bearer token: open https://uniform.app/cli-login in the browser and press copy. Alternatively, use a Uniform API key via the x-api-key header.

  2. Create the file below in a project that has a .env with your Uniform project ID and API key. Replace the Authorization header with your copied value (Bearer <value>) and the projectId in the URL with your own.

  3. Run it with npx ts-node file.ts.

Proceed with caution. This code removes compositions without confirmation. Make a backup of the project first.

import { CanvasClient } from '@uniformdev/canvas';
import dotenv from 'dotenv';
dotenv.config();

const getCompositions = async (authorizationHeader: string) => {
  const url =
    '<https://uniform.app/api/v1/canvas?skipPatternResolution=true&skipOverridesResolution=true&state=0&withComponentIDs=false&keyword=&pattern=false&orderBy=updated_at_DESC&offset=0&limit=5&withUIStatus=true&withProjectMapNodes=true&attachedToProjectMap=false&withTotalCount=true&projectId=e05ecb95-10f6-4e05-912c-41dceecf4304>';

  try {
    const response = await fetch(url, {
      method: 'GET',
      headers: {
        Authorization: authorizationHeader,
      },
    });

    if (!response.ok) {
      throw new Error('Failed to fetch compositions');
    }

    const data = await response.json();
    const compositions = data.compositions.map(
      (composition: { composition: { _id: string } }) => composition.composition._id
    );
    return compositions;
  } catch (error) {
    console.error('Error fetching compositions:', error);
    return [];
  }
};

const main = async () => {
  const compositionIds = await getCompositions(
    // put your token here
    'Bearer eyJhb...'
  );

  console.log('Found', compositionIds.length, 'compositions');
  console.log(compositionIds);

  const client = new CanvasClient({
    apiKey: process.env.UNIFORM_API_KEY,
    projectId: process.env.UNIFORM_PROJECT_ID,
  });

  compositionIds.forEach((compositionId: string) => {
    console.log('Removing', compositionId);
    client.removeComposition({ compositionId: compositionId });
  });

  console.log('done');
};

main();

The query uses attachedToProjectMap=false to return only compositions not attached to a project map node, with limit=5 per run — adjust as needed.