MongoDB $skip Stage

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

What Is $skip?

$skip is a MongoDB aggregation stage that discards the first N documents from the current pipeline and passes the rest through—the offset half of offset pagination.

Remember
$skip = "drop the first N, keep the rest"

{ $skip: 5 }

does NOT sort by itself
for pages → $sort → $skip → $limit
skip = (page - 1) × pageSize
{ $skip: 0 } → skip nothing

large N → expensive (walks past every skipped doc)
find().skip(N) → similar idea outside aggregation
UsePrefer
Offset pagination$skip + $limit
Stable page order$sort before $skip
Deep pages (huge offset)Cursor / range pagination (not large $skip)

How to Use $skip in MongoDB

Filter, sort for a stable order, skip the offset, then limit the page size.

  1. Optionally filter with $match.
  2. $sort so pages stay consistent.
  3. Add { $skip: N } where N = (page - 1) × pageSize.
  4. Add $limit for the page size.
PieceRequired?Role
N (integer)YesHow many documents to discard
Prior $sortNo*Stable order for pagination
Following $limitNo*Page size after the offset

*Required in practice for meaningful, stable pages.

Pseudocode

Pseudocode
count = 0
for each document from prior stage:
  if count < N:
    count += 1
    continue          // discard
  emit document       // keep everything after N

Cheat Sheet

Keep these facts handy while building paginated lists.

TopicDetail
TypeAggregation stage
Syntax{ $skip: N }
Page pattern$sort → $skip → $limit
Offset formula(page - 1) × pageSize
Page 2, size 3$skip: 3, $limit: 3
Skip 0No-op (page 1)
PerformanceLarge skips still scan discarded docs

Live Preview

Skip published articles (sorted by views) and keep the next 3 (teaching model of $sort + $skip + $limit).

Published only, sorted by views descending, page size 3.

Result
page = 1, skip = 0, limit = 3

1. Node.js + MongoDB     15200
2. Intro to MongoDB      12400
3. REST API Design       11300

Worked Walkthrough — Page 2

Seven published articles sorted by views. Goal: page 2 with 3 per page.

Calculation
$match published: true
  → 7 articles

$sort views: -1
  → 15200, 12400, 11300, 8900, 7800, 6700, 5400

$page = 2, pageSize = 3
$skip = (2 - 1) × 3 = 3

$skip: 3
  → drop first three (Node.js, Intro, REST)

$limit: 3
  → Aggregation Basics (8900)
     Express Middleware  (7800)
     Indexing Strategies (6700)

MongoDB Examples

We use a small articles collection. First skip a few after sorting, then build a full page with $limit. Run these in mongosh after inserting the sample data once.

Example 1 — Sample Data

Eight articles with views and a published flag—enough for skip and page demos.

mongosh
db.articles.drop();
db.articles.insertMany([
  { title: "Intro to MongoDB",    category: "Database", views: 12400, published: true },
  { title: "Aggregation Basics",  category: "Database", views: 8900,  published: true },
  { title: "Node.js + MongoDB",   category: "Backend",  views: 15200, published: true },
  { title: "Indexing Strategies", category: "Database", views: 6700,  published: true },
  { title: "Draft: Sharding",     category: "Database", views: 0,     published: false },
  { title: "REST API Design",     category: "Backend",  views: 11300, published: true },
  { title: "Schema Validation",   category: "Database", views: 5400,  published: true },
  { title: "Express Middleware",  category: "Backend",  views: 7800,  published: true }
]);

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps eight documents in articles. Run find() to see them:

mongosh
db.articles.find({}, { title: 1, views: 1, published: 1, _id: 0 })

Read the important fields like this:

FieldExamplePlain English
views15200Sort key for stable pages
publishedtrueFilter before skipping
title"REST API Design"What we show in results

How It Works

  1. Reset once. drop() keeps demos repeatable.
  2. Mixed publish state. One draft is excluded by later $match.
  3. find() shows the same shape. Ready for $skip next.

Example 2 — Skip After Sort

Goal: published articles, highest views first, drop the top 3 and show what remains (no page size yet).

mongosh
db.articles.aggregate([
  { $match: { published: true } },
  { $sort: { views: -1 } },
  { $skip: 3 },
  { $project: { title: 1, views: 1, _id: 0 } }
]);

How It Works

  1. Filter. $match drops the unpublished draft.
  2. Rank. $sort puts highest views first.
  3. Offset. $skip: 3 discards Node.js, Intro, and REST API.
  4. Rest remain. Without $limit, every document after the skip is returned.

Example 3 — Pagination

Goal: page 2 with 3 articles per page, still sorted by views.

mongosh
const page = 2;
const pageSize = 3;
const skip = (page - 1) * pageSize;   // 3

db.articles.aggregate([
  { $match: { published: true } },
  { $sort: { views: -1 } },
  { $skip: skip },
  { $limit: pageSize },
  { $project: { title: 1, views: 1, _id: 0 } }
]);

// Page 1 would be skip: 0, limit: 3
// → Node.js, Intro, REST API
// Page 2 → Aggregation Basics, Express, Indexing

How It Works

  1. Same order every page. $sort first so pages do not reshuffle.
  2. Skip the previous page. Page 2 skips the first 3 rows.
  3. Limit the page size. $limit keeps the next 3 only.
  4. Large skips. Very high offsets get expensive—use cursors for deep pages.

Edge Cases & Pitfalls

Check these before shipping paginated APIs with $skip.

order

$skip does not sort

Without $sort, the offset is in pipeline order—pages can reshuffle between requests.

cost

Large skips are expensive

Skipping 100,000 rows still walks past them. Prefer cursor/range pagination for deep pages.

ties

Add a tie-breaker for stable pages

Equal views can shuffle. Sort by views then _id (or createdAt) for consistency.

pair

Skip alone is rarely a page

Without $limit, everything after the offset is returned. Pair them for real pages.

Performance Notes

ApproachCostBest for
Small $skip + $limitFine for early pagesTypical UI pagination
$match then skipFewer docs to walkFiltered lists
Huge $skipReads discarded rowsAvoid—use cursors

Put selective $match first, keep $sort before $skip, and avoid growing offsets for deep navigation.

Key Takeaways

  • Role: discard the first N documents.
  • Pages: $sort → $skip → $limit.
  • Formula: skip = (page - 1) × pageSize.
  • Caution: large skips get expensive.

One line: $skip answers “start after the first N documents.”

Frequently Asked Questions

$skip discards the first N documents from the pipeline and passes the rest to the next stage. Use it to offset results—for example, skip the first 20 rows to show page 2 of a paginated list.
{ $skip: N } where N is a non-negative integer. Example: { $skip: 10 } drops the first 10 documents. { $skip: 0 } skips nothing.
Always $sort first for consistent order, then $skip, then $limit. Page 1 (size 5): skip 0, limit 5. Page 2: skip 5, limit 5. Formula: skip = (page - 1) × pageSize.
Yes. $skip applies to documents in their current pipeline order. Put $sort before $skip when pagination must be deterministic. Put $match before $sort to filter first.
Yes. MongoDB must walk past every skipped document. For deep pages, prefer cursor-based pagination (filter by last _id or sort key) instead of ever-growing $skip values.
$skip removes documents from the start (offset). $limit caps how many continue (page size). Together they implement offset pagination; neither sorts—you need $sort for that.

Next: $sort

Order pipeline documents so skip and limit stay stable.

$sort tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful