You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FR] Add Next and Previous Icon to switch easily between 2 objects #12397
Please verify that this feature request has NOT been suggested before.
I checked and didn't find a similar feature request
Problem statement
Hi,
when we want to change information in multiple object (for example 30 parts) , we have to filter list, click on the first object, change the value, return to the list , open the second object, change the value ...
Suggested solution
I think it's a good idea to add two icons on banner "Previous" and "Next" for simplify navigation between the filter list objects
When object change, the focus field must be the same.
we have quite a few ways to filter in the API so there are a few things that need to be considered when implementing something like this; would probably be an intresting addtion.
I invite interested collaborators to propose their technical solution (is the filter in a local session, in the url, what is the user story when the list of items suddenly is shorter then the current index). A successful PR for this is a generic enough approach that it also works for frontend plugins using the components exposed by the lib (Instance, Page/PageDetail) and has tests ensure most common user and failure scenarios keep working.
Where filter state lives: capture it at click-time, not at the detail page. When a row is clicked in InvenTreeTable, store { endpoint, pks: number[], sourceKey } (the current page's ordered result pks) in a small zustand slice, same pattern DetailDrawer.tsx already uses for detailDrawerStack. A useNextPrevInstance hook (peer to useInstance) resolves prev/next as O(1) lookups into that array when it matches the current endpoint+pk - this is next/prev in what the user was actually looking at, not just pk__lt/pk__gt. No stored context (direct link, plugin usage) falls back to an ordering-aware pk__gt/pk__lt query, so it still works standalone.
List shrinks mid-navigation: if a resolved neighbor pk 404s, catch it in the destination's useInstance query, drop that pk from the stored context, and recurse to the next one instead of dead-ending.
Generic for plugins: nextPrev becomes an optional prop on PageDetail itself ({hasPrev, hasNext, onPrev, onNext}), not per-page wiring - any page or plugin using PageDetail gets it for free by calling the hook, no copy-pasted filter logic.
Happy to implement this if it sounds like the right direction.
From looking at the proposals I see a few common issues:
behavior currently is bound to the browser/session - opening the url presented in the ui should produce the same result in a new browser
the implementation should stire in a way where the logic is implemented in the generic components so that plugins do not need to re-implement / provide state for this logic. Similar to the approach that is taken with the InvenTreeTable component plugin authors should not need to be aware of this when they use the default apiEndpoint and filtering approaches provided by the generic lookup methods
ordering when clicking next/prev needs to be the same as when vieweing in the list view and consider all filters and ordering arguments that can be applied in the list view
I will review PRs once it is confirmed per comment/in the description these things are fulfilled
Shareable via URL. The detail URL carries three reserved params: _navApi (the list
endpoint), _nav (the list query string) and _navIndex (the record's offset in that
result set). Nothing lives in session storage or router state, so pasting the URL into a
fresh browser gives the same prev/next set.
Same filters and ordering as the list._nav is generated from getTableFilters(false) in InvenTreeTable, which is the same call that builds the
table's own request, minus limit/offset. The params are therefore identical to the list
view by construction rather than by re-deriving them, and there is no exclusion list to
keep in sync as filters are added.
Prev and next resolve with a single request against the same endpoint:
GET <_navApi>?<_nav>&limit=3&offset=<_navIndex - 1>
which returns [prev, current, next] along with the total count.
I have deliberately avoided pk__gt / pk__lt. That only gives the correct neighbour when
the list is ordered by pk, and it quietly returns the wrong record as soon as someone
sorts by name or any other column. It would also need new filter fields adding per model,
whereas limit/offset is available on every list endpoint already, since
LimitOffsetPagination is the global default. So no backend change and no API version bump.
Generic, so plugins get it for free. The context is written in InvenTreeTable's click
handlers and read in PageDetail, so any page built on the standard components picks it up
without supplying state of its own. _navApi is carried explicitly rather than looked up
from ModelInformationDict, because plugin endpoints are not in that dict. It is validated
as a relative /api/ path when read.
When the list is shorter than the index. The three-record window is self checking. If
the middle record is not the one being viewed, the list has moved. If the current pk sits
elsewhere in the window, which is the usual case when a single record has been deleted,
the offset is corrected and navigation continues. If it is not in the window at all, the
buttons are hidden rather than pointing at a guess. Same for endpoints that are not
paginated, where the offset has no meaning.
One thing worth flagging: with ENABLE_PREVIEW_PANEL on, which is the default, a row click
opens the preview drawer rather than the detail page. openPreview already accepts a targetUrl argument that the table does not currently pass, so the drawer's View Details
link can carry the same context. Without that, the buttons rarely appear at all on a
default install.
Tests would cover navigating from a filtered and sorted list, crossing a page boundary,
first and last record, a deleted neighbour, a URL opened cold in a new context, and a
detail page reached directly with no context at all.
no new PRs will be accepted; i will review the proposed once tonight and close any that do no follow the inatructions or are not passing tests.
If any PRs meet rquiremnets the bounty will be awarded
Hi there — I saw this bounty and I'd like to take a shot at it. I'll fork the repository, implement a focused fix, and open a PR referencing this issue with a clear description of the change and how it was verified. I'm comfortable iterating on review feedback. Thanks!
Hi @matmair , I've implemented the previous/next navigation described in this issue and opened PR #12655. I'd like to work on this bounty. Could you assign it to me on BountyHub so I can claim it? Happy to iterate on the PR based on your review.
@chulakseb this feature was merged; you can check it out in the demo within the next 24hours and decide if you are happy with it (regarding features and the bounty)
💰 Congratulations toJr-kennyfor successfully completing the bounty!
Your hard work and dedication have paid off, and you have earned the bounty reward.
Thank you for your contribution to the project! 🙌
Activity
SchrodingersGat commentedon Jul 14, 2026
How do you propose to determine what is "next" and "previous" in any given context?
chulakseb commentedon Jul 15, 2026
I sugest to save the filter on object list and allow next and previous only for the filtered items
Example :
save the research field, filtered value and select category, on the screeeshot I've 55 items / 358
matmair commentedon Jul 30, 2026
we have quite a few ways to filter in the API so there are a few things that need to be considered when implementing something like this; would probably be an intresting addtion.
I invite interested collaborators to propose their technical solution (is the filter in a local session, in the url, what is the user story when the list of items suddenly is shorter then the current index). A successful PR for this is a generic enough approach that it also works for frontend plugins using the components exposed by the lib (Instance, Page/PageDetail) and has tests ensure most common user and failure scenarios keep working.
matmair commentedon Jul 30, 2026
I have created a bounty of 20$ for this from my money; @chulakseb feel free to increase the pledge if you are able to https://www.bountyhub.dev/en/bounty/view/a157ff08-20cf-466a-9ab3-a321f515133f
bountyhub-bot commentedon Jul 30, 2026
🚀 Bounty Alert!
💰 A bounty of $20.00 has been created by matmair on BountyHub for this issue.
🔗 Claim this bounty by submitting a pull request that solves this issue!
You can also increase the amount of the bounty if you think the issue is worth more.
Good luck, and happy coding! 💻
chidii commentedon Jul 31, 2026
Proposal for the two open questions above:
Where filter state lives: capture it at click-time, not at the detail page. When a row is clicked in InvenTreeTable, store
{ endpoint, pks: number[], sourceKey }(the current page's ordered result pks) in a small zustand slice, same pattern DetailDrawer.tsx already uses for detailDrawerStack. A useNextPrevInstance hook (peer to useInstance) resolves prev/next as O(1) lookups into that array when it matches the current endpoint+pk - this is next/prev in what the user was actually looking at, not just pk__lt/pk__gt. No stored context (direct link, plugin usage) falls back to an ordering-aware pk__gt/pk__lt query, so it still works standalone.List shrinks mid-navigation: if a resolved neighbor pk 404s, catch it in the destination's useInstance query, drop that pk from the stored context, and recurse to the next one instead of dead-ending.
Generic for plugins: nextPrev becomes an optional prop on PageDetail itself (
{hasPrev, hasNext, onPrev, onNext}), not per-page wiring - any page or plugin using PageDetail gets it for free by calling the hook, no copy-pasted filter logic.Happy to implement this if it sounds like the right direction.
matmair commentedon Jul 31, 2026
From looking at the proposals I see a few common issues:
I will review PRs once it is confirmed per comment/in the description these things are fulfilled
4 remaining items
Protonicwave commentedon Aug 7, 2026
Shareable via URL. The detail URL carries three reserved params:
_navApi(the listendpoint),
_nav(the list query string) and_navIndex(the record's offset in thatresult set). Nothing lives in session storage or router state, so pasting the URL into a
fresh browser gives the same prev/next set.
Same filters and ordering as the list.
_navis generated fromgetTableFilters(false)in InvenTreeTable, which is the same call that builds thetable's own request, minus limit/offset. The params are therefore identical to the list
view by construction rather than by re-deriving them, and there is no exclusion list to
keep in sync as filters are added.
Prev and next resolve with a single request against the same endpoint:
which returns [prev, current, next] along with the total count.
I have deliberately avoided pk__gt / pk__lt. That only gives the correct neighbour when
the list is ordered by pk, and it quietly returns the wrong record as soon as someone
sorts by name or any other column. It would also need new filter fields adding per model,
whereas limit/offset is available on every list endpoint already, since
LimitOffsetPagination is the global default. So no backend change and no API version bump.
Generic, so plugins get it for free. The context is written in InvenTreeTable's click
handlers and read in PageDetail, so any page built on the standard components picks it up
without supplying state of its own.
_navApiis carried explicitly rather than looked upfrom ModelInformationDict, because plugin endpoints are not in that dict. It is validated
as a relative /api/ path when read.
When the list is shorter than the index. The three-record window is self checking. If
the middle record is not the one being viewed, the list has moved. If the current pk sits
elsewhere in the window, which is the usual case when a single record has been deleted,
the offset is corrected and navigation continues. If it is not in the window at all, the
buttons are hidden rather than pointing at a guess. Same for endpoints that are not
paginated, where the offset has no meaning.
One thing worth flagging: with ENABLE_PREVIEW_PANEL on, which is the default, a row click
opens the preview drawer rather than the detail page.
openPreviewalready accepts atargetUrlargument that the table does not currently pass, so the drawer's View Detailslink can carry the same context. Without that, the buttons rarely appear at all on a
default install.
Tests would cover navigating from a filtered and sorted list, crossing a page boundary,
first and last record, a deleted neighbour, a URL opened cold in a new context, and a
detail page reached directly with no context at all.
Does this look like the right direction?
matmair commentedon Aug 9, 2026
no new PRs will be accepted; i will review the proposed once tonight and close any that do no follow the inatructions or are not passing tests.
If any PRs meet rquiremnets the bounty will be awarded
matmair commentedon Aug 11, 2026
most PRs did not even pass the style tests
mitre88 commentedon Aug 16, 2026
Hi there — I saw this bounty and I'd like to take a shot at it. I'll fork the repository, implement a focused fix, and open a PR referencing this issue with a clear description of the change and how it was verified. I'm comfortable iterating on review feedback. Thanks!
matmair commentedon Aug 16, 2026
@mitre88 we are not taking any more submissions on this. Your work will not be merged, you will not receive a bounty
bountyhub-bot commentedon Aug 16, 2026
🚀 Bounty Update!
💰 A contribution of $20.00 has been made by chulakseb on BountyHub for this issue.
The total amount of the bounty is now $40
🔗 Claim this bounty by submitting a pull request that solves this issue!
Good luck, and happy coding! 💻
chulakseb commentedon Aug 16, 2026
Is it possible to test what you have developped? Link, login ?
Excuse me, I’m not familiarised with GitHub 😅
matmair commentedon Aug 17, 2026
You would have to pull the PR locally into you test setup and run it there
Lescure1004 commentedon Aug 18, 2026
Hi @matmair , I've implemented the previous/next navigation described in this issue and opened PR #12655. I'd like to work on this bounty. Could you assign it to me on BountyHub so I can claim it? Happy to iterate on the PR based on your review.
matmair commentedon Aug 18, 2026
@Lescure1004 we are not taking submissions as per above comment
matmair commentedon Aug 19, 2026
@chulakseb this feature was merged; you can check it out in the demo within the next 24hours and decide if you are happy with it (regarding features and the bounty)
chulakseb commentedon Aug 20, 2026
I have tested in the demo, it's very nice and useful . Thanks a lot to all contributors !
bountyhub-bot commentedon Aug 20, 2026
🎉 Bounty Awarded!
💰 Congratulations to Jr-kenny for successfully completing the bounty!
Your hard work and dedication have paid off, and you have earned the bounty reward.
Thank you for your contribution to the project! 🙌