Skip to main content
Read Lists let you organize books in a specific reading order, perfect for:
  • Crossover events spanning multiple series
  • Recommended reading orders for complex universes
  • Personal “to-read” queues
  • Curated book club selections
Unlike Collections which group entire series, Read Lists contain individual books in a defined sequence.

List read lists

Get all read lists with pagination:
import { getReadLists } from 'komga-sdk';

const result = await getReadLists({
  client,
  query: { page: 0, size: 20 },
});

if (result.data) {
  console.log(`Found ${result.data.totalElements} read lists`);
  for (const readList of result.data.content) {
    console.log(`${readList.name} - ${readList.bookIds.length} books`);
  }
}

Get a read list by ID

Retrieve details and the ordered book list:
import { getReadListById } from 'komga-sdk';

const result = await getReadListById({
  client,
  path: { readListId: 'readlist-123' },
});

if (result.data) {
  console.log(`Read List: ${result.data.name}`);
  console.log(`Books in order: ${result.data.bookIds.length}`);
}

Create a read list

Create a new read list with books in specific order:
import { createReadList } from 'komga-sdk';

const result = await createReadList({
  client,
  body: {
    name: 'Crisis on Infinite Earths Reading Order',
    summary: 'Complete crossover event reading order',
    ordered: true, // Books must be read in this sequence
    bookIds: [
      'book-001', // Issue #1
      'book-002', // Issue #2
      'book-003', // Tie-in: Superman
      'book-004', // Issue #3
      // ...
    ],
  },
});

if (result.data) {
  console.log(`Created read list: ${result.data.name}`);
}
Use ordered: true for reading orders where sequence matters. Use ordered: false for thematic groupings without a required order.

Update a read list

Modify the read list name, summary, or book order:
import { updateReadListById } from 'komga-sdk';

const result = await updateReadListById({
  client,
  path: { readListId: 'readlist-123' },
  body: {
    name: 'Updated Reading Order',
    summary: 'Revised reading order with additional tie-ins',
    ordered: true,
    bookIds: ['book-001', 'book-002', 'book-005', 'book-006'],
  },
});

Delete a read list

Remove a read list (books themselves are not deleted):
import { deleteReadListById } from 'komga-sdk';

await deleteReadListById({
  client,
  path: { readListId: 'readlist-123' },
});

Get books in a read list

Retrieve the actual book objects in reading order:
import { getBooksByReadListId } from 'komga-sdk';

const result = await getBooksByReadListId({
  client,
  path: { readListId: 'readlist-123' },
  query: { page: 0, size: 50 },
});

if (result.data) {
  // Books are returned in the read list order
  for (const book of result.data.content) {
    console.log(`${book.metadata.title} (${book.metadata.number})`);
  }
}

Find read lists containing a book

Identify which read lists include a specific book:
import { getReadListsByBookId } from 'komga-sdk';

const result = await getReadListsByBookId({
  client,
  path: { bookId: 'book-123' },
  query: { page: 0, size: 20 },
});

if (result.data) {
  for (const list of result.data.content) {
    console.log(`${list.name} (${list.bookIds.length} books)`);
  }
}

Import ComicRack lists

Use ComicRack .cbl lists to build read lists automatically:
import { matchComicRackList } from 'komga-sdk';

const file = new File([cblBlob], 'reading-order.cbl', { type: 'text/xml' });

const result = await matchComicRackList({
  client,
  body: { file },
});

if (result.data) {
  // result.data contains matches and recommendations
  console.log(`Matched ${result.data.matches.length} books`);
}

Thumbnails

Get read list thumbnail

Retrieve the thumbnail for a read list:
import { getReadListThumbnail } from 'komga-sdk';

const result = await getReadListThumbnail({
  client,
  path: { readListId: 'readlist-123' },
});

if (result.data) {
  const thumbnailUrl = URL.createObjectURL(result.data);
}

Upload custom thumbnail

Add a custom thumbnail to represent the read list:
import { addUserUploadedReadListThumbnail } from 'komga-sdk';

const file = new File([imageBlob], 'thumbnail.jpg', { type: 'image/jpeg' });

await addUserUploadedReadListThumbnail({
  client,
  path: { readListId: 'readlist-123' },
  body: { file },
});

Delete custom thumbnail

Remove a user-uploaded thumbnail:
import { deleteUserUploadedReadListThumbnail } from 'komga-sdk';

await deleteUserUploadedReadListThumbnail({
  client,
  path: { readListId: 'readlist-123' },
});
Komga provides endpoints to navigate through books in a read list:

Get next book

import { getBookSiblingNextInReadList } from 'komga-sdk';

const result = await getBookSiblingNextInReadList({
  client,
  path: { 
    readListId: 'readlist-123',
    bookId: 'book-001' 
  },
});

if (result.data) {
  console.log(`Next: ${result.data.metadata.title}`);
} else {
  console.log('End of read list');
}

Get previous book

import { getBookSiblingPreviousInReadList } from 'komga-sdk';

const result = await getBookSiblingPreviousInReadList({
  client,
  path: { 
    readListId: 'readlist-123',
    bookId: 'book-002' 
  },
});

if (result.data) {
  console.log(`Previous: ${result.data.metadata.title}`);
} else {
  console.log('Beginning of read list');
}

Common workflows

Build a crossover reading order

async function createCrossoverReadList(eventName: string, bookIds: string[]) {
  const result = await createReadList({
    client,
    body: {
      name: `${eventName} Reading Order`,
      summary: `Complete reading order for ${eventName}`,
      ordered: true,
      bookIds,
    },
  });

  return result.data;
}

// Example: Create a reading order for a major event
const crisisBooks = [
  'book-crisis-01',
  'book-crisis-02', 
  'book-superman-tiein-01',
  'book-crisis-03',
  'book-batman-tiein-01',
  'book-crisis-04',
];

await createCrossoverReadList('Infinite Crisis', crisisBooks);

Create a “Next Up” read list

import { getBooks, createReadList } from 'komga-sdk';

async function createNextUpReadList(userId: string) {
  // Get books with progress
  const booksResult = await getBooks({
    client,
    body: { 
      readStatus: 'UNREAD' 
    },
    query: { 
      page: 0, 
      size: 20,
      sort: ['metadata.releaseDate,desc']
    },
  });

  if (!booksResult.data) return;

  const bookIds = booksResult.data.content.map(b => b.id);
  
  await createReadList({
    client,
    body: {
      name: 'My Next Reads',
      summary: 'Recently released books to read',
      ordered: false,
      bookIds,
    },
  });
}

Update progress through a read list

async function getReadingProgress(readListId: string) {
  // Get all books in the read list
  const booksResult = await getBooksByReadListId({
    client,
    path: { readListId },
    query: { unpaged: true },
  });

  if (!booksResult.data) return;

  const books = booksResult.data.content;
  const totalBooks = books.length;
  
  // Count read books
  const readBooks = books.filter(b => 
    b.readProgress?.read === true
  ).length;
  
  const progress = {
    total: totalBooks,
    read: readBooks,
    percentage: Math.round((readBooks / totalBooks) * 100),
  };

  console.log(`Progress: ${progress.read}/${progress.total} (${progress.percentage}%)`);
  return progress;
}

Find read lists containing a book

async function findBookInReadLists(bookId: string) {
  // Get all read lists
  const readListsResult = await getReadLists({
    client,
    query: { unpaged: true },
  });

  if (!readListsResult.data) return [];

  // Filter to read lists containing this book
  const containingReadLists = readListsResult.data.content.filter(
    list => list.bookIds.includes(bookId)
  );

  console.log(`Book appears in ${containingReadLists.length} read lists`);
  return containingReadLists;
}

Comparison: Collections vs Read Lists

FeatureCollectionsRead Lists
ContainsSeriesIndividual books
OrderingOptional (ordered flag)Optional (ordered flag)
Use caseGroup related seriesDefine reading sequences
Example”Marvel Events""Civil War Reading Order”
NavigationBrowse seriesNext/previous book helpers

Next steps