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.
2. Remove them in bulk via the API (for long lists)
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-keyheader.Create the file below in a project that has a
.envwith your Uniform project ID and API key. Replace the Authorization header with your copied value (Bearer <value>) and theprojectIdin the URL with your own.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.