Skip to content

Media: Add a checkbox to toggle infinite scrolling in the media modal - #12795

Open
itzmekhokan wants to merge 3 commits into
WordPress:trunkfrom
itzmekhokan:fix/65775-media-modal-infinite-scroll-toggle
Open

Media: Add a checkbox to toggle infinite scrolling in the media modal#12795
itzmekhokan wants to merge 3 commits into
WordPress:trunkfrom
itzmekhokan:fix/65775-media-modal-infinite-scroll-toggle

Conversation

@itzmekhokan

@itzmekhokan itzmekhokan commented Jul 31, 2026

Copy link
Copy Markdown

Adds a point-of-use control for infinite scrolling in the attachments browser, as a follow-up to #65564.

What the problem was:

  • Infinite scrolling is enabled by default as of [62632], but the only way to change it is the "Infinite Scrolling" option on the user profile screen. Users who need the "Load more" button for accessibility, or who want to avoid loading a large library, have to leave the modal, change their profile, and come back.

What the fix does:

  • Renders an "Infinite scrolling" checkbox before the list of media items, in both the media modal and the Media Library grid view, that turns infinite scrolling on and off immediately.
  • Saves the change as the user's preference, so it persists beyond the current view. A new save-media-infinite-scrolling AJAX action writes the same infinite_scrolling personal option as the profile screen, and the two stay in sync in both directions.
  • Confirms the change next to the checkbox and sends the same message to wp.a11y.speak(), because the controls that change are at the end of the list and usually out of view.
  • Unchecking it reveals the "Load more" button and the item count and stops the scroll handler from requesting more attachments. Checking it hides them and resumes loading on scroll.
  • Keeps the screen reader search-results message in sync with the active mode.

Approach and why:

  • The checkbox is only rendered when saving the personal option takes effect. wp_enqueue_media() passes a new canToggleInfiniteScrolling setting, which is false when a media_library_infinite_scrolling filter callback overrides the option, or when there is no user to save it for. Announcing "Preference saved" for a value a filter overrides on the next page load would be misleading, so no control is offered in that case. This is deliberately blunt: any callback on the filter hides the control, even one that only applies conditionally.
  • The media_library_infinite_scrolling filter and the profile option still determine the initial state, and their documented precedence is unchanged.
  • infiniteScrolling moves from a module-level constant to per-instance state on AttachmentsBrowser, and is passed down to the Attachments view rather than read from the global a second time.
  • The scroll handler is now always bound and bails when infinite scrolling is off, so it is live if the user turns scrolling on mid-session.
  • To make room for a control before the list, .attachments-wrapper is now the positioned, scrolling region in both modes rather than only when the "Load more" button is present. This reuses the offsets that already applied to the wrapper at every breakpoint, so no per-breakpoint pixel values change.

Trac ticket: https://core.trac.wordpress.org/ticket/65775

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Ticket analysis, tests and generate PR. All changes were reviewed and validated by me.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

Infinite scrolling of the attachments list can now be turned on and off at
the point of use, from a checkbox rendered before the list of media items in
both the media modal and the Media Library grid view.

The checkbox reflects the value resolved by `wp_enqueue_media()` and
overrides it for the current view only, so the
`media_library_infinite_scrolling` filter and the "Infinite Scrolling" user
profile option still determine the initial state. Turning the checkbox off
reveals the "Load more" button and stops the scroll handler from requesting
more attachments; turning it back on hides the button and resumes loading
on scroll.

To make room for the checkbox before the list, the attachments wrapper is
now the positioned, scrolling region in both modes, rather than only when
the "Load more" button is present.

Fixes #65775.
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props khokansardar, joedolson, tyxla, annezazu, wildworks, fcoveram, joen.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@joedolson joedolson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works well within a limited scope, but I think it needs to save the user's preference, rather than just persisting for the duration of the modal. Along with that, notices that inform the user of the preference toggle would be needed.

I pushed a minor visual improvement to fix alignments.

Comment thread src/js/media/views/attachments/browser.js Outdated
this.infiniteScrolling = infiniteScrolling;
this.attachments.options.infiniteScrolling = infiniteScrolling;
this.$el.toggleClass( 'has-load-more', ! infiniteScrolling );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may be helpful to include some kind of visual affordance that confirms that something has happened, since all the control changes happen out of the viewport.

I also think that the control needs to save this preference for the user, so one possibility would be a notice that appears/is spoken saying "Infinite scrolling preference saved"

…owser.

The checkbox that turns infinite scrolling on and off now writes the same
"Infinite Scrolling" personal option as the profile screen, through a new
`save-media-infinite-scrolling` AJAX action, so the choice persists beyond
the current view.

Changing the checkbox displays a confirmation next to it and sends the same
message to `wp.a11y.speak()`, since the "Load more" button it reveals is at
the end of the list of attachments and is usually out of view.

Because the control saves that personal option, it is only rendered when
saving the option takes effect. `wp_enqueue_media()` now reports whether a
`media_library_infinite_scrolling` filter callback overrides the option, or
there is no user to save it for.

The label is shortened to "Infinite scrolling", as the checked state conveys
whether it is enabled.

See #65775.
@itzmekhokan
itzmekhokan requested a review from joedolson July 31, 2026 23:55
@itzmekhokan

Copy link
Copy Markdown
Author

Thanks for the review — all three points are addressed in c763743.

  • The label is now just "Infinite scrolling".
  • The checkbox saves the preference. A new save-media-infinite-scrolling AJAX action writes the same infinite_scrolling personal option as the profile screen, so the control and the profile screen stay in sync in both directions.
  • Changing it shows a confirmation next to the checkbox and sends the same message to wp.a11y.speak(): "Infinite scrolling disabled. Load more button displayed. Preference saved." and "Infinite scrolling enabled. Preference saved."

One decision worth your input. Since the control saves the personal option, wp_enqueue_media() now passes a canToggleInfiniteScrolling setting, and the checkbox is not rendered when a media_library_infinite_scrolling callback overrides that option — otherwise it would announce "Preference saved" for a value the filter resets on the next page load. That is deliberately blunt: any callback on the filter hides the control, even one that only applies conditionally. If you would rather keep the control visible and suppress only the notice in that case, I am happy to change it.

@annezazu

annezazu commented Aug 1, 2026

Copy link
Copy Markdown

Tested this out and the experience feels a bit rough. It doesn't automatically update when you change your preference. The alignment of the text for the option is also off, both for the initial "Infinite scrolling" and what I think is the description text when you select an option (it seems like this still isn't fixed?)

Screenshot 2026-08-01 at 2 55 24 PM

The double infinite scrolling text feels repetitive too. Let's get some @WordPress/gutenberg-design help here in terms of phrasing and placement! We want it to fit in well with other approaches WordPress takes.

@joedolson

Copy link
Copy Markdown
Contributor

@annezazu In my testing, I thought it wasn't switching modes when I checked it, but it actually did; it was just slightly difficult to tell in my test environment, for two reasons: there wasn't a sharp difference between the number loaded in infinite scroll and the total, and because if you've already scrolled to the end, not much actually changes - there's nothing more to load. Can you double check that? (My testing was on the previous iteration of the PR, so it is possible that it's regressed since I checked.)

@annezazu

annezazu commented Aug 1, 2026

Copy link
Copy Markdown

I tested with 87 photos on a site and if I turned it off, the load more never appeared. I had to navigate away and back for it to show.

I almost wonder if a better option is to have a dismissible notice that tells folks there’s an option available to control this in your profile options rather than permanently placing this here. Again, I want to hear from more designers!

@t-hamano

t-hamano commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

To be perfectly honest, I feel this toggle is too conspicuous and might unnecessarily complicate things. Furthermore, I question whether the average user is familiar with the term "infinite scrolling." I'm also hesitant to make such a significant code change right before RC1.

image

My proposal is to simply revert the default behavior of infinite scrolling to "disabled" without adding a toggle, while still maintaining the user option. This allows users to maintain the previous default behavior while offering an option for users who prefer infinite scrolling.

image

I also wonder if this setting needs to be on the fly. Dynamically switching infinite scroll is a feature that hasn't been around before, and as mentioned, it's hard to tell if dynamically switching infinite scroll is actually working unless there are a lot of images, and it seems like it would only confuse users.

@annezazu

annezazu commented Aug 2, 2026

Copy link
Copy Markdown

That’s against what @m has explicitly asked for (on by default with the option to disable). This is a project leadership call that’s already been made.

checkbox = $( '<input />', {
type: 'checkbox',
id: id,
checked: this.infiniteScrolling

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to feedback that has been given previously, we should make sure to consider newly rendered media browsers, since they seem to discard the saved choice if it differs from the one during page-load.

To test this:

  • Open Featured Image > Media Library with scrolling disabled.
  • Enable it and wait for "Preference saved".
  • Switch to Upload Files, then back to Media Library.
  • The recreated checkbox is disabled again, while the live-region message still says it was enabled and saved.

Comment on lines +515 to +518
checkbox.on( 'change', function() {
view.toggleInfiniteScrolling( this.checked );
view.saveInfiniteScrolling( this.checked );
} );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've noticed that quick changes can persist the opposite of the final checkbox state. Every change immediately launches an independent AJAX request. If an enable request and a subsequent disable request execute out of order, the old value can become the final value.

view.updateInfiniteScrollingStatus(
infiniteScrolling ?
__( 'Infinite scrolling enabled. Preference saved.' ) :
__( 'Infinite scrolling disabled. Load more button displayed. Preference saved.' )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The success text always announces "Load more button displayed", but the button is kept hidden when the library is empty or hasMore() is false. Can be reproduced with an empty library: no button appeared, but both the visible status and wp.a11y.speak() reported that it did. We may want to use a generic disabled message or condition it on the actual button state.


$infinite_scrolling = wp_validate_boolean( wp_unslash( $_POST['infiniteScrolling'] ) );

update_user_meta( get_current_user_id(), 'infinite_scrolling', $infinite_scrolling ? 'true' : 'false' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs multisite coverage. get_user_option() prefers a blog-prefixed option before the global user meta, but the AJAX handler updates only global infinite_scrolling meta. If a prefixed value exists, which is particularly plausible on multisite, the endpoint reports success while reload continues using the unchanged prefixed value. I believe we should either use global meta consistently, matching the profile screen, or explicitly handle the higher-priority site option.

Comment thread src/wp-includes/media.php
* option, so it is only offered when saving that option takes effect: there
* has to be a user to save it for, and no filter callback overriding it.
*/
$can_toggle_infinite_scrolling = is_user_logged_in() && ! has_filter( 'media_library_infinite_scrolling' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we only check the filter, and as a consequence the toggle appears in attachment browsers where pagination is meaningless. Rendering is controlled only by one global setting, so every AttachmentsBrowser receives the checkbox, including Edit Gallery, playlist/collection editing, and Edit Selection browsers backed by fixed Selection collections. See GalleryEdit and editSelectionContent(). There, toggling cannot reveal meaningful pagination but still changes a global preference and announces that the current view changed. We might want to make this an explicit per-browser option enabled only for pageable library queries.


update_user_meta( get_current_user_id(), 'infinite_scrolling', $infinite_scrolling ? 'true' : 'false' );

wp_send_json_success();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears like the return value of update_user_meta() is ignored and wp_send_json_success() runs unconditionally. A failed write therefore produces "Preference saved".

@fcoveram

fcoveram commented Aug 3, 2026

Copy link
Copy Markdown

Agree with @t-hamano about the settings location and the familiarity with the term. To me, this belongs to a system preference, similar to changing from light to dark mode and increasing UI contrast. Therefore, I would expect this in the wp-admin settings.

Aki's example shows it in General, but it makes more sense to me in Media just before the "Image sizes" heading. Also, if the setting only impacts the modal view and the grid layout can not be modified, then the copy could be more explicit:

Infinite scroll: [ ] Enable infinite scrolling in modal

The above is a suggestion in addition to showing it on by default, as Anne said.


I might be missing some context here, but the latest changes use DataViewsPicker and the modern UI, but I see the previous in this PR.

If the setting is agreed to be displayed on this screen, and the use of DataViewsPicker remains, I would place it in the View options menu.

CleanShot 2026-08-03 at 11 16 01@2x

@jasmussen

jasmussen commented Aug 3, 2026

Copy link
Copy Markdown

Thanks for the attention to detail.

In balancing leadership direction as far as defaults, with preferences that enable options to accommodate everyone, immediately coming to mind is our desire for a welcome-modal that through an out-of-box experience can let everyone quickly set their personal preferences. Jay Koster explores this in-depth, noting that not one size fits all.

That's a long term consideration. Important to know about to contextualise what we do, but not a near-term fix.

A user-level opt-out already exists. My understanding is that this opt-out is suggested as being not-discoverable-enough. The way to improve this discoverability is through slow and sustained iteration of the IA to ensure that the same things live in the same places. Users > Profile is perhaps a confusing place for finding things like your colour schemes. But Fran with his mention of Dark Mode alludes to a solution: with the admin-bar everywhere, I would expect a much more prominent and improved CTA for configuring user preferences in that dropdown, clearer than simply "Edit Profile":

profile

It's not in a great place at the moment, with "admin" and "Edit Profile" being two lines of text inside the same link. As a drop-down menu, I'd expect "Admin" being text, "Edit Profile" and "Log Out" to be menu items. I'd even suggest that "Edit profile" item could be a button with a clearer label, and perhaps help text: "Customise your profile, preferred colour scheme, and interaction settings." That's an iterative design task, worth doing regardless of where we land.


For the approach outlined in this PR, let's be clear however what it is we're building: it's a per-view user preference.

Importantly, that has precedence:

  • The incoming DataViewsPicker and media modal experiments (which also feature infinite scroll options), as well as DataViews: both provide per-view options. Users might simply want to mix and match based on view: for Media, infinite scroll might be a more glanceable way to find what you're looking for, whereas for Pages you might prefer pagination with 20 items per page in a comfortable density.
  • Posts, Pages, Comments feature a Screen Options tab, which similarly lets you configure items per page and density.

In both cases, per-view options make sense. The challenge here is that the present default media library has no view options: there's no "Screen Options", and there's no Appearance control like in DataViews has:

dataviews

This omission feels like a compromise in the name of its modal variant, moreso than an intentional and considered design choice.

Because of that, and absent a simple way to introduce a screen options tab for Media, the solution would be to add a view-config button similar to what DataViews has: reuse the same pattern, and put the infinite scroll toggle inside that. Infinite scroll, items per page, view density: all are valid per-view controls, and none of them deserve the prominence of permanent visibility above the list of media.

@t-hamano

t-hamano commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Is it possible to move that toggle to the toolbar?

media-1 media-2

I might be missing some context here, but the latest changes use DataViewsPicker and the modern UI, but I see the previous in this PR.

The dataview-based media modal is still an experimental feature.

@jasmussen

Copy link
Copy Markdown

Is it possible to move that toggle to the toolbar?

From a purely personal opinion, but ultimately deferring to leads chiming in, this feels like a bandaid solution. The most direct pattern to follow is a cog button that opens a menu, with the toggle inside.

@fcoveram

fcoveram commented Aug 3, 2026

Copy link
Copy Markdown

Thanks for the thoughtful response @jasmussen

I second you approach, and +1 to this

…The most direct pattern to follow is a cog button that opens a menu, with the toggle inside.


To @t-hamano

The dataview-based media modal is still an experimental feature.

You're right, my mistake. I confused it with the media edit in modal 🙃.

@joedolson

Copy link
Copy Markdown
Contributor

There are two key things in the recent threads that I think are key:

  1. The user profile is a good place for user settings to live. However, the user profile labeling and context isn't well exposed, leading it to not appear like a natural place for users to go to manager their user preferences. While that's outside the scope of what should be targeted for 7.1, I think it would be an important change to look at for 7.2; it would make a lot of things more sensible.

  2. Placing a tool for setting options in the media library does actually make some sense. The media library isn't currently designed for that, but I suggest it could live next to the media tabs or next to the close button, for ready discoverability.

@t-hamano

t-hamano commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I'm unsure how to proceed with this PR.

Because of that, and absent a simple way to introduce a screen options tab for Media, the solution would be to add a view-config button similar to what DataViews has: reuse the same pattern, and put the infinite scroll toggle inside that. Infinite scroll, items per page, view density: all are valid per-view controls, and none of them deserve the prominence of permanent visibility above the list of media.

Should we perhaps try this approach once?

@t-hamano

t-hamano commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I believe that some implementation is necessary for the upcoming release of 7.1 RC3 next week. To move this forward, let's first discuss and agree on a general approach that seems reasonable from both design and accessibility perspectives.

One idea I had was to add a toggle button to display the settings for the media library as a dialog. I would appreciate your feedback.

#12932

@jasmussen

Copy link
Copy Markdown

Thanks for your efforts. The approach you took in 12932 is maybe not visually in the best place, but conceptually it uses mostly the same language as DataViews does, which is important from a "same things in the same places" perspective. So if it can build a consensus, I can support it on the path to more iteration in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants