Opens in a new tab
  1. Home
  2. Guides
  3. Backend
Backend guide

WordPress List-Table Sorting, Explained

Learn how WordPress list-table sorting works, how sortable columns connect to orderby and query logic, how to sort custom metadata correctly and how to avoid common performance and implementation mistakes.

  • Updated September 14, 2026
  • 22 min read
  • WordPress guide

WordPress list-table sorting is the system that lets users click certain column headings in wp-admin to change the order in which records are displayed.

You see this behavior throughout WordPress administration.

Depending on the screen, you may be able to sort by:

  • title;
  • author;
  • date;
  • username;
  • email address;
  • comment author;
  • custom metadata;
  • plugin-defined values.

A sortable column usually appears with a clickable heading and an ascending or descending indicator.

But WordPress list-table sorting has two distinct layers:

1. Tell the interface
   that a column is sortable

2. Tell the underlying query
   how that value should actually be sorted

Those two steps are related, but they are not the same thing.

A developer can make a column heading appear sortable without correctly modifying the underlying query. The interface may then generate an orderby request that the query does not understand.

This guide explains how WordPress list-table sorting works, how sortable columns are registered, how orderby and order are passed through administration requests, how to sort custom metadata correctly, why numeric and textual sorting produce different results and how to avoid expensive or misleading custom sorting implementations.

What is a WordPress list table?

WordPress administration repeatedly uses tabular interfaces for collections of objects.

Examples include:

  • Posts;
  • Pages;
  • Users;
  • Comments;
  • Plugins;
  • Media in List view;
  • taxonomies;
  • custom post types;
  • plugin-defined management screens.

These interfaces commonly provide:

  • columns;
  • row actions;
  • bulk actions;
  • filters;
  • search;
  • pagination;
  • Screen Options;
  • sorting.

For the broader architecture, see WordPress Admin List Tables, Explained.

What makes a column sortable?

A normal WordPress column may simply display information.

For example:

Title
Featured Image
Author
SEO Score
Price

Only some of those columns may be sortable.

A sortable column tells WordPress:

This heading can generate
an ordering request.

The underlying list-table system then creates URLs containing query parameters such as:

orderby=title
order=asc

or:

orderby=date
order=desc

The orderby parameter identifies what should be sorted

Consider an administration URL such as:

/wp-admin/edit.php?
post_type=post
&orderby=title
&order=asc

The important parameters are:

orderby
order

orderby identifies the value used for sorting.

order determines the direction.

ASC and DESC control the direction

The two normal values are:

ASC
DESC

Conceptually:

ASC
→ A to Z
→ smallest to largest
→ oldest to newest

DESC
→ Z to A
→ largest to smallest
→ newest to oldest

The exact meaning depends on the data type being sorted.

WordPress uses WP_List_Table for much of this interface

Core’s:

WP_List_Table

class contains the infrastructure used by many WordPress administration tables.

The official WP_List_Table reference documents the base list-table class.

One of its methods is:

get_sortable_columns()

The official WP_List_Table::get_sortable_columns() documentation describes the format used to define sortable columns.

A sortable column maps a visible column to an orderby value

The simplest structure is conceptually:

'column_name' => 'orderby_value'

For example:

'title' => 'title'

This means:

visible column:
title

query ordering value:
title

The two names do not necessarily have to be identical.

WordPress exposes sortable columns through a screen-specific filter

Core applies a dynamic filter with the pattern:

manage_{$screen_id}_sortable_columns

The official sortable-columns hook documentation explains that the dynamic part is the current WordPress screen ID.

For the normal Posts screen, the screen ID is:

edit-post

so the filter becomes:

manage_edit-post_sortable_columns

Custom post types get their own screen IDs

Suppose you register:

post_type = property

The list-table screen ID is normally:

edit-property

and the sortable-column filter becomes:

manage_edit-property_sortable_columns

This lets developers customize sorting independently for different content types.

Example: register a custom sortable column

Suppose a custom post type contains a column:

Price

with the internal column key:

property_price

You can register it as sortable:

function myplugin_sortable_property_columns(
    $columns
) {

    $columns['property_price'] =
        'property_price';

    return $columns;
}

add_filter(
    'manage_edit-property_sortable_columns',
    'myplugin_sortable_property_columns'
);

This tells the list table that:

property_price

can be used as an ordering choice.

Registering the column is only the first half

This is the critical part.

WordPress can now generate:

orderby=property_price

but WP_Query does not automatically know what:

property_price

means.

You still need to map that custom ordering value to something the query understands.

Core fields often work without additional query logic

WP_Query already supports many standard ordering values.

The official WP_Query documentation includes supported values such as:

date
title
name
author
modified
parent
ID
menu_order
comment_count
meta_value
meta_value_num

Therefore, a column that maps directly to a supported Core value may require little additional query work.

Custom metadata needs additional handling

Suppose:

property_price

is stored as post meta:

meta_key = property_price

You need to modify the administration query when:

orderby=property_price

is requested.

pre_get_posts is commonly used for post list sorting

WordPress provides:

pre_get_posts

before a WP_Query query executes.

The official pre_get_posts documentation describes how the query object can be modified before SQL is generated.

Example: sort a custom metadata column

function myplugin_property_admin_sorting(
    $query
) {

    if ( ! is_admin() ) {
        return;
    }

    if (
        'property' !==
        $query->get( 'post_type' )
    ) {
        return;
    }

    if (
        'property_price' !==
        $query->get( 'orderby' )
    ) {
        return;
    }

    $query->set(
        'meta_key',
        'property_price'
    );

    $query->set(
        'orderby',
        'meta_value_num'
    );
}

add_action(
    'pre_get_posts',
    'myplugin_property_admin_sorting'
);

Now the workflow becomes:

User clicks Price
↓
orderby=property_price
↓
pre_get_posts detects it
↓
meta_key=property_price
↓
orderby=meta_value_num
↓
WP_Query builds the correct ordering

Why meta_value_num matters for numbers

Metadata is commonly stored as text at the database level.

If you sort numeric-looking values as strings, you may get:

1
10
100
2
20
3

instead of:

1
2
3
10
20
100

For numeric metadata, use:

meta_value_num

where appropriate.

The official WP_Query documentation specifically distinguishes meta_value from meta_value_num.

Text sorting and numeric sorting are not interchangeable

Use:

meta_value

for textual values such as:

Bronze
Gold
Premium
Silver

Use:

meta_value_num

for numeric values such as:

15
49
299
1250

when those values are actually stored as numeric-compatible metadata.

Dates stored in metadata need deliberate formatting

A date such as:

14/09/2026

is convenient for humans.

It is less convenient for reliable database ordering.

A normalized representation such as:

20260914

or an appropriate database datetime format is generally easier to sort predictably.

The storage format should be chosen when designing the data model, not patched later in the list-table interface.

WP_Query supports meta_type too

Modern WP_Query can cast metadata using:

meta_type

with supported SQL types such as:

NUMERIC
CHAR
DATE
DATETIME
DECIMAL
SIGNED
UNSIGNED

This can be useful when metadata needs a particular comparison or ordering type.

Named meta_query clauses can also be sorted

WP_Query supports ordering by named clauses inside:

meta_query

For more complex queries, a clause can be given a key and then referenced through:

orderby

This provides more flexibility than a single global:

meta_key

when several metadata conditions participate in the query.

Sorting by metadata can be substantially more expensive

A Core field such as:

post_date

exists directly in:

wp_posts

A custom property such as:

property_price

may live in:

wp_postmeta

Sorting by it can therefore require additional joins and database work.

Do not make every custom column sortable automatically

A sortable column should provide enough value to justify the query cost.

Useful candidates might include:

  • price;
  • priority;
  • event date;
  • registration date;
  • last activity;
  • numeric score.

Less useful candidates may include:

  • large formatted descriptions;
  • remote API status;
  • generated HTML;
  • computed values requiring many queries;
  • values without a meaningful natural order.

The visible value and sortable value can be different

Suppose a column displays:

€1,250.00

The interface should not attempt to sort the formatted string:

€1,250.00

Instead, the underlying query should sort the raw value:

1250

Formatting belongs to presentation.

Sorting belongs to the data layer.

Never sort by the HTML rendered in the column

A list-table callback may output:

<strong>High Priority</strong>

but that rendered HTML is not what WordPress should use for SQL ordering.

The correct model is:

Database value
→ query ordering

Formatted output
→ visual presentation

Sorting happens before rows are rendered

The database query determines which rows arrive and in what order.

Only afterward does WordPress render each row and its columns.

Conceptually:

Request
↓
Query parameters
↓
Database ordering
↓
Result set
↓
List-table rendering

You cannot reliably fix database ordering inside a column-rendering callback because the result set has already been retrieved.

WordPress can define an initial sorting direction

The sortable-column definition can contain additional information beyond:

'column' => 'orderby'

The official WP_List_Table::get_sortable_columns() documentation supports arrays that can define properties such as an initial sort direction.

Depending on the definition, the initial interaction can prefer:

ASC

or:

DESC

Choose the default direction based on the meaning of the data

For alphabetical names:

ASC

is often natural.

For dates:

DESC

is often more useful because recent records appear first.

For revenue:

DESC

may surface the largest values first.

The default should match the user’s likely question.

Do not hardcode $_GET directly into SQL

A sorting request ultimately originates from URL parameters.

Never take:

$_GET['orderby']

and concatenate it directly into a SQL statement.

A request such as:

?orderby=...

is user-controlled input.

Prefer recognized query values

A safer architecture is:

requested alias
↓
whitelist / map
↓
known query parameter

For example:

property_price
→ meta_key property_price
→ meta_value_num

rather than allowing arbitrary SQL identifiers to arrive from the URL.

Custom SQL requires separate security handling

If a custom list table uses raw SQL instead of WP_Query, validate:

  • allowed ordering fields;
  • allowed directions;
  • table names;
  • dynamic values.

For example:

$allowed_orderby = array(
    'name',
    'date_created',
    'priority',
);

$orderby = 'date_created';

if (
    isset( $_GET['orderby'] )
    &&
    in_array(
        $_GET['orderby'],
        $allowed_orderby,
        true
    )
) {
    $orderby =
        $_GET['orderby'];
}

Direct database access should follow the broader practices described in How to Safely Run SQL Queries in WordPress.

WP_List_Table custom implementations need their own query logic

If you build a custom class extending:

WP_List_Table

you commonly override:

get_columns()
get_sortable_columns()
prepare_items()

The sortable definition controls the interface.

Your:

prepare_items()

implementation still needs to apply the requested order to the data source.

Example custom WP_List_Table sortable definition

protected function get_sortable_columns() {

    return array(
        'name' => array(
            'name',
            false
        ),
        'created' => array(
            'created_at',
            true
        ),
    );
}

This tells WordPress which column headings should behave as sorting controls.

Then prepare_items() must honor those values

A custom implementation might retrieve:

orderby
order

and map them to a safe, known database query.

The exact code depends on the data source.

The key principle is:

UI definition
and
query implementation

must agree.

Sorting WordPress users follows a different query API

Not every administration screen uses WP_Query.

The Users screen uses the WordPress user-query system.

Custom sorting there may require:

WP_User_Query

and user-specific hooks.

This is why generic code that assumes every list table is powered by posts can fail on other wp-admin screens.

Comments have their own list-table sorting definitions

WordPress Core’s comments list table defines sortable columns such as:

author
response
date

internally mapping those visible columns to values such as:

comment_author
comment_post_ID
comment_date

This illustrates the separation between:

visible column key
and
underlying sort value

Sorting can interact with filters

An administration list may already be filtered by:

  • post status;
  • category;
  • taxonomy;
  • date;
  • author;
  • custom metadata;
  • search term.

Sorting should normally reorder that filtered result set rather than discard the filtering state.

Preserve the current list-table context

When WordPress generates sortable headings, it preserves relevant query parameters so a user can move from:

Published posts
sorted by date

to:

Published posts
sorted by title

without unexpectedly returning to all content.

Custom interfaces should follow the same principle.

Sorting and pagination must agree

Suppose the list contains:

10,000 records

with:

50 per page

The sorting must be performed at the database-query level before pagination.

The correct sequence is:

filter
↓
sort
↓
paginate
↓
render

not:

fetch page 1
↓
sort those 50 rows in PHP

Client-side sorting is not equivalent to list-table sorting

JavaScript could sort the rows already rendered in the browser.

But that only reorders:

the current page

rather than:

the complete dataset

For a paginated WordPress administration list, server-side sorting is normally the correct model.

Sorting by custom post meta can exclude records unexpectedly

This deserves testing.

Some metadata-based query patterns can affect posts that do not contain the requested meta key.

For example:

Post A
property_price = 100

Post B
property_price = 200

Post C
no property_price metadata

Depending on the query construction, Post C may not participate in the same way as A and B.

Before deploying custom metadata sorting, test:

  • records with values;
  • records with zero;
  • records with empty values;
  • records where the meta key does not exist.

Null, empty and missing values need a policy

Decide where these should appear:

missing
empty
0
null-like value

For example:

priced properties first
unpriced properties last

may require more sophisticated query logic than a basic meta_value_num order.

Natural business order may not be alphabetical order

Imagine a workflow status:

Critical
High
Medium
Low

Alphabetical sorting produces:

Critical
High
Low
Medium

which does not represent the business priority.

In that case you may need:

  • a numeric priority value;
  • a separate sort key;
  • custom SQL ordering;
  • a purpose-built data model.

Store sortable values in sortable formats

If a field will be used frequently for sorting, design the storage accordingly.

Examples:

Price
→ numeric value

Date
→ normalized date representation

Priority
→ integer rank

Status
→ explicit sortable rank if business order matters

Do not store everything as human-facing formatted strings and then expect efficient database sorting later.

Custom database tables can be better for specialized datasets

If an application needs to sort and filter millions of structured records by many different fields, generic WordPress metadata may not be the ideal data model.

Custom tables can provide:

  • dedicated typed columns;
  • appropriate indexes;
  • purpose-built query patterns;
  • more predictable ordering.

The database model should follow the application requirements rather than forcing every dataset through wp_postmeta.

Indexes influence sorting performance

Sorting large result sets can become expensive when the database cannot efficiently locate and order the relevant values.

Performance depends on:

  • table size;
  • filter conditions;
  • joins;
  • indexes;
  • selected ordering field;
  • number of rows returned.

A slow sortable column should be profiled as a database-query problem rather than fixed by hiding the column.

Do not add indexes without measuring the query

A good process is:

identify slow request
↓
inspect generated query
↓
inspect execution plan
↓
review existing indexes
↓
test proposed change

Adding indexes to random metadata columns without understanding the query can increase storage and write cost without solving the relevant bottleneck.

Sorting should be deterministic where possible

Suppose several records have the same:

price = 100

If no secondary ordering exists, their internal order may not be meaningful.

For predictable interfaces, it can be useful to add a secondary order such as:

price ASC
title ASC

or:

priority DESC
date DESC

WP_Query supports multiple orderby values

WP_Query can accept an array of ordering rules in supported contexts.

Conceptually:

orderby = array(
    'menu_order' => 'ASC',
    'title'      => 'ASC',
)

This can make ordering more stable when several records share the primary value.

Do not modify every admin query accidentally

This is one of the most common mistakes with:

pre_get_posts

A callback such as:

function modify_query(
    $query
) {
    $query->set(
        'orderby',
        'meta_value'
    );
}

without appropriate guards can affect many unrelated queries.

Scope custom ordering carefully

Check relevant conditions such as:

is_admin()

post type

requested orderby

main query where appropriate

current screen when necessary

For example:

if ( ! is_admin() ) {
    return;
}

if (
    'property' !==
    $query->get( 'post_type' )
) {
    return;
}

if (
    'property_price' !==
    $query->get( 'orderby' )
) {
    return;
}

Do not force a custom order when the user selected another one

If the user clicks:

Title

your plugin should not continue forcing:

Price

unless that behavior is deliberately part of the application.

A sorting callback should normally activate only for its own recognized orderby alias.

Default ordering and interactive sorting are separate

A custom post type may have a default administration order such as:

menu_order ASC

while still allowing users to click:

Title
Date
Price

A well-designed implementation distinguishes:

no explicit sort selected
→ use application default

explicit sortable column selected
→ respect user choice

Screen Options affect visibility, not sorting logic

A user may hide a sortable column through Screen Options.

That changes what they see.

It does not fundamentally redefine how the query system works.

For the relationship between list-table columns and personal visibility preferences, see WordPress Screen Options vs. Custom Columns and WordPress Screen Options, Explained.

Custom Content Columns can improve list-table information density

TheOneWP Custom Content Columns can customize the information displayed in supported WordPress content lists.

This solves a different problem from query sorting.

The distinction is:

Custom column
→ what information is visible

Sortable column
→ whether the dataset can be ordered by it

Query implementation
→ how that ordering actually happens

Adding a column should therefore not automatically imply that it should also become sortable.

Custom Sort Order solves another separate problem

TheOneWP Custom Sort Order addresses persistent manual content ordering.

That is different from clicking a list-table heading.

Compare:

List-table sorting
→ temporary viewing order

Custom content order
→ stored editorial order

For example, an administrator might permanently arrange:

Service A
Service B
Service C

for frontend presentation while temporarily sorting the wp-admin list alphabetically to locate a record.

Do not save list-table sorting as content order accidentally

Clicking:

Title ↑

should normally alter only the current administration view.

It should not rewrite:

  • menu_order;
  • post metadata;
  • taxonomy order;
  • frontend presentation order.

Sorting a view and changing application data are fundamentally different operations.

Test sorting with realistic data

A custom sortable column can appear correct with:

10 test records

and perform badly with:

100,000 production records

Testing should include:

  • large datasets;
  • duplicate values;
  • missing values;
  • zero values;
  • long text;
  • different roles;
  • pagination;
  • filters;
  • search queries.

Verify both directions

Test:

ASC

and:

DESC

A column that appears correct in one direction can expose incorrect null handling or string comparison behavior in the other.

Verify the URL parameters

When clicking a sortable heading, inspect the administration URL.

You should see values such as:

orderby=property_price
order=asc

Then confirm the server-side query maps those values correctly.

Inspect the generated database query when debugging

If ordering behaves incorrectly, inspect the actual SQL rather than guessing from the visible rows.

Questions include:

  • Which table is being ordered?
  • Is wp_postmeta joined?
  • Is the value cast numerically?
  • Is the requested meta key correct?
  • Are missing metadata rows excluded?
  • Is another plugin changing orderby?

Plugins can conflict over admin queries

Several plugins may hook into:

pre_get_posts

or lower-level SQL filters.

A sorting bug may therefore arise from:

  • priority conflicts;
  • one plugin overwriting another query variable;
  • global query modifications;
  • incorrect post-type checks;
  • unexpected meta joins.

Use posts_orderby only when higher-level APIs are insufficient

WordPress exposes the:

posts_orderby

filter for modifying the generated SQL ORDER BY clause.

The official posts_orderby documentation explains that the filter runs before the post-retrieving SQL statement executes.

This is useful for advanced ordering logic.

But it operates closer to SQL and should not be the first choice when normal WP_Query parameters already express the required ordering.

Prefer the highest-level API that solves the problem

A reasonable order of preference is:

WP_Query orderby parameters
↓
meta query ordering
↓
pre_get_posts
↓
specialized SQL filters
↓
custom SQL

Move lower only when the higher-level API cannot represent the required behavior.

Sorting checklist for developers

  • Identify the correct WordPress screen ID.
  • Register only genuinely useful sortable columns.
  • Use the appropriate manage_{$screen_id}_sortable_columns filter.
  • Map the visible column to a known internal orderby value.
  • Implement the corresponding query logic.
  • Use Core ordering values when possible.
  • Use meta_value for textual metadata where appropriate.
  • Use meta_value_num for numeric metadata.
  • Consider meta_type when explicit casting is needed.
  • Store sortable data in appropriate formats.
  • Do not sort formatted HTML.
  • Do not concatenate arbitrary orderby values into SQL.
  • Whitelist raw-SQL ordering fields.
  • Scope pre_get_posts callbacks carefully.
  • Respect explicit user sorting choices.
  • Keep default ordering separate from interactive sorting.
  • Test missing metadata.
  • Test duplicate values.
  • Test ASC and DESC.
  • Test pagination.
  • Test filters and search together with sorting.
  • Profile metadata sorting on realistic datasets.
  • Inspect SQL when results are unexpected.
  • Use lower-level SQL filters only when necessary.

Sorting checklist for site administrators

  • Click column headings only when they display sorting indicators.
  • Check whether the current screen supports sorting for the field you need.
  • Remember that sorting changes the current view, not the stored content order.
  • Use filters before sorting large datasets when possible.
  • Keep useful sortable columns visible through Screen Options.
  • Report columns that visually appear sortable but return incorrect results.
  • Check whether numeric values are being sorted numerically.
  • Check whether records with missing values disappear unexpectedly.
  • Do not assume slow sorting means the entire WordPress database is slow.

Related guides

Final recommendation

WordPress list-table sorting is best understood as a collaboration between the administration interface and the query behind it.

The complete flow is:

Register column
↓
Mark column as sortable
↓
WordPress generates orderby + order
↓
Query interprets the requested value
↓
Database sorts the result set
↓
Pagination is applied
↓
List table renders the rows

Registering a sortable heading is therefore only half the implementation.

If the value already maps to a supported WordPress query field such as title, date or ID, Core can often perform the ordering directly. If the column represents metadata or another custom value, the query must explicitly map the requested orderby value to the appropriate data source and comparison type.

Use numeric sorting for numbers, normalized representations for dates, meaningful secondary ordering where duplicate values are common and careful query scoping so custom administration behavior does not leak into unrelated screens.

Most importantly, keep three concepts separate:

Column display
→ what users see

List-table sorting
→ how the current view is ordered

Stored content order
→ persistent application data

When those layers remain separate, sortable WordPress administration columns become genuinely useful instead of merely decorative controls attached to queries that do not understand what was clicked.

Simplify your WordPress stack

A modular WordPress toolkit. 104 focused tools.

Ultimately, you can build cleaner workflows, maintain fewer plugins and enable only the features each website actually needs.