Issue #21166
Bug: Home redirect always lands on first object alphabetically, ignores last-visited
draft pr openDescription
Going to the workspace root (/) always redirects me to the same object: the first one alphabetically by nameSingular. It doesn't matter which object I visited last, and clearing browser cache/localStorage makes no difference. In my workspace a custom object called "Activities" (nameSingular: "activity") always wins because "a" sorts first.
Steps to reproduce
- Have multiple objects, with one that sorts first alphabetically by
nameSingular. - Visit a different object's index page, e.g. Companies.
- Go to the workspace root
/. - Expected: redirect to the last-visited object (Companies). Actual: redirect to the alphabetically-first object.
Root cause
useDefaultHomePagePath uses lastVisitedObjectMetadataItem if it's set, otherwise it falls back to the first of readableNonSystemObjectMetadataItems, which is sorted with a.nameSingular.localeCompare(b.nameSingular). The last-visited branch never fires, for two reasons.
First, lastVisitedObjectMetadataItemIdState is created via createAtomState({ useLocalStorage: true }), which calls atomWithStorage(key, default, undefined, undefined), so getOnInit is unset and defaults to false in jotai 2.x. The atom is only ever accessed imperatively through store.get/store.set and is never subscribed via a hook, so it never mounts or hydrates. On a fresh load store.get(...) returns the default null and the code falls through to the alphabetical fallback. Note that the useSessionStorage and useCookieStorage branches in the same factory do pass { getOnInit: true }, only the useLocalStorage branch doesn't.
Second, defaultHomePagePath is a useMemo that reads the atom imperatively and doesn't list it as a dependency, so it isn't reactive to in-session updates either.
Suggested fix
Pass { getOnInit: true } for the useLocalStorage branch in createAtomState, or for this atom specifically, so store.get returns the persisted value. Optionally have useDefaultHomePagePath subscribe to the atom (e.g. useAtomValue) and add it to the memo deps so it stays reactive. Worth adding a regression test that sets a last-visited id and asserts the redirect resolves to that object, since the current tests only cover the fallback.
Environment
- Twenty
main@ 6b5a956 (2026-06-02) - jotai ^2.17.1
We installed dependencies and ran lint, typecheck, build, and test in a sandbox — all passed.
An AI estimate of whether the change actually fixes the issue — the checks themselves only confirm nothing broke. It's a hint for review, not a pass/fail check.
Why?
The diff fixes both root causes identified in the issue: it adds `getOnInit: true` to the `useLocalStorage` branch in `createAtomState.ts` so `lastVisitedObjectMetadataItemIdState` hydrates on init, and changes `useDefaultHomePagePath.ts` to read that atom reactively via `useAtomStateValue` instead of `store.get`, also adding it to the `useCallback` dependencies so the redirect updates when the value changes. A regression test verifies the last-visited redirect behavior works.
lintpassed · 24.3s
[7m[1m[36m NX [39m[22m[27m [36mRunning target [1mlint[22m for project twenty-front and [1m3[22m tasks it depends on:[39m [2m-[22m twenty-front [2m[36m[39m[22m [2m> [22m[2mnx run[22m twenty-shared:generateBarrels [2m[local cache][22m [2m> [22mtsx packages/twenty-shared/scripts/generateBarrels.ts [2m> [22m[2mnx run[22m twenty-oxlint-rules:build [2m[local cache][22m [2m> [22mnpx esbuild packages/twenty-oxlint-rules/oxlint-plugin.ts --bundle --format=esm --platform=node --outfile=packages/twenty-oxlint-rules/dist/oxlint-plugin.mjs "--define:__filename='\"[plugin]\"'" "--define:__dirname='\"[plugin]\"'" "--banner:js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);" npm warn Unknown env config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. packages/twenty-oxlint-rules/dist/oxlint-plugin.mjs 251.0kb ⚡ Done in 30ms [2m> [22m[2mnx run[22m twenty-shared:build [2m[local cache][22m [2m> [22mnpx vite build npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. [2m> [22mtsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. [2m> [22m[2mnx run[22m twenty-front:lint [2m> [22mnpx oxlint --type-aware -c .oxlintrc.json src/ && (npx oxfmt --check src/ || (echo 'ERROR: oxfmt formatting check failed! Fix with: npx nx lint twenty-front --configuration=fix' && false)) npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. Found 0 warnings and 0 errors. Finished in 17.3s on 7480 files with 29 rules using 3 threads. npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. Checking formatting... All matched files use the correct format. Finished in 2867ms on 7749 files using 3 threads. [2m[32m[39m[22m [7m[1m[32m NX [39m[22m[27m [32mSuccessfully ran target [1mlint[22m for project twenty-front and [1m3[22m tasks it depends on[39m [2mNx read the output from the cache instead of running the command for 3 out of 4 tasks.[22mtypecheckpassed · 81.6s
.ts' 'dist/utils/**/*.d.ts.map' && npx rollup -c rollup.config.sdk-dts.mjs npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. [36m [1msrc/sdk/define/index.ts[22m → [1mdist/define/index.d.ts[22m...[39m [32mcreated [1mdist/define/index.d.ts[22m in [1m2.9s[22m[39m [36m [1msrc/sdk/front-component/index.ts[22m → [1mdist/front-component/index.d.ts[22m...[39m [32mcreated [1mdist/front-component/index.d.ts[22m in [1m1.4s[22m[39m [36m [1msrc/sdk/billing/index.ts[22m → [1mdist/billing/index.d.ts[22m...[39m [32mcreated [1mdist/billing/index.d.ts[22m in [1m941ms[22m[39m [36m [1msrc/sdk/logic-function/index.ts[22m → [1mdist/logic-function/index.d.ts[22m...[39m [32mcreated [1mdist/logic-function/index.d.ts[22m in [1m1.4s[22m[39m [36m [1msrc/sdk/utils/index.ts[22m → [1mdist/utils/index.d.ts[22m...[39m [32mcreated [1mdist/utils/index.d.ts[22m in [1m789ms[22m[39m [2m> [22m[2mnx run[22m twenty-front-component-renderer:build [2m> [22mnpx rimraf dist && npx vite build -c vite.config.ts npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. [2m> [22mtsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist npm warn Unknown env config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "dangerously-allow-all-builds". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. npm warn Unknown user config "verify-deps-before-run". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options. [2m> [22m[2mnx run[22m twenty-front:typecheck [2m> [22mtsgo -p tsconfig.json [2m[32m[39m[22m [7m[1m[32m NX [39m[22m[27m [32mSuccessfully ran target [1mtypecheck[22m for project twenty-front and [1m8[22m tasks it depends on[39m [2mNx read the output from the cache instead of running the command for 5 out of 9 tasks.[22m
buildpassed · 141.4s
[39m[1m[2m 429.34 kB[22m[1m[22m[2m │ gzip: 143.43 kB[22m [2mbuild/[22m[2massets/[22m[36mnative-48B9X9Wg.js [39m[1m[2m 432.75 kB[22m[1m[22m[2m │ gzip: 82.80 kB[22m [2mbuild/[22m[2massets/[22m[36mblocknote-uAyHRr_h.js [39m[1m[2m 433.64 kB[22m[1m[22m[2m │ gzip: 131.02 kB[22m [2mbuild/[22m[2massets/[22m[36mSpreadsheetImport-CVEh_ndi.js [39m[1m[2m 444.33 kB[22m[1m[22m[2m │ gzip: 152.40 kB[22m [2mbuild/[22m[2massets/[22m[36mInter_18pt-Regular-byxnNS-8-BnplJFJY.js [39m[1m[2m 456.96 kB[22m[1m[22m[2m │ gzip: 204.79 kB[22m [2mbuild/[22m[2massets/[22m[36mInter_18pt-Bold-BOnnSImi-hPpeqQwn.js [39m[1m[2m 458.93 kB[22m[1m[22m[2m │ gzip: 208.13 kB[22m [2mbuild/[22m[2massets/[22m[36mInter_18pt-Italic-BVnfHlUD-BNrjkzHH.js [39m[1m[2m 462.03 kB[22m[1m[22m[2m │ gzip: 212.52 kB[22m [2mbuild/[22m[2massets/[22m[36mInter_18pt-BoldItalic-DPKIpVzB-SGWoqiFX.js [39m[1m[2m 464.29 kB[22m[1m[22m[2m │ gzip: 216.29 kB[22m [2mbuild/[22m[2massets/[22m[36mDocumentViewer-ypWTPoYb.js [39m[1m[2m 534.31 kB[22m[1m[22m[2m │ gzip: 170.27 kB[22m [2mbuild/[22m[2massets/[22m[36mSettingsGraphQLPlayground-CmhTY-Gg.js [39m[1m[2m 652.36 kB[22m[1m[22m[2m │ gzip: 207.56 kB[22m [2mbuild/[22m[2massets/[22m[36mFrontComponentRenderer-DLjVfkB-.js [39m[1m[2m1,019.01 kB[22m[1m[22m[2m │ gzip: 242.09 kB[22m [2mbuild/[22m[2massets/[22m[36mAllIcons-B093-FjO-YZocWUPU.js [39m[1m[2m1,305.19 kB[22m[1m[22m[2m │ gzip: 243.12 kB[22m [2mbuild/[22m[2massets/[22m[36mexportBlockNoteEditorToPdf-DqGPSCBM.js [39m[1m[2m1,504.68 kB[22m[1m[22m[2m │ gzip: 501.77 kB[22m [2mbuild/[22m[2massets/[22m[36mindex-D33Oq4y8.js [39m[1m[2m2,217.74 kB[22m[1m[22m[2m │ gzip: 636.39 kB[22m [2mbuild/[22m[2massets/[22m[36mindex-BaVvKbEl.js [39m[1m[2m3,340.57 kB[22m[1m[22m[2m │ gzip: 861.14 kB[22m [2mbuild/[22m[2massets/[22m[36mget-function-input-schema-DTlcRJz3-BuUwaHjC.js [39m[1m[2m3,593.27 kB[22m[1m[22m[2m │ gzip: 1,029.77 kB[22m [2mbuild/[22m[2massets/[22m[36mindex-DVvTod4c.js [39m[1m[2m6,255.95 kB[22m[1m[22m[2m │ gzip: 1,722.76 kB[22m [32m✓ built in 2m 13s[39m [linaria/wyw] ===== CSS PRE-BUILD SUMMARY ===== [linaria/wyw] Files transformed: 1016 [linaria/wyw] Files skipped (no @linaria): 15664 [linaria/wyw] Transform time: 30839ms [linaria/wyw] Avg per transformed file: 30.4ms [linaria/wyw] Slow files (>10x avg = 304ms): [linaria/wyw] 774ms /src/modules/layout-customization/components/LayoutCustomizationBarMenuDropdown.tsx [linaria/wyw] 763ms /src/modules/layout-customization/components/LayoutCustomizationBar.tsx [linaria/wyw] 410ms /src/modules/error-handler/components/AppRootErrorFallback.tsx [linaria/wyw] 338ms /src/modules/object-record/record-table/record-table-body/components/RecordTableBodyVirtualizedDraggableClone.tsx [linaria/wyw] 312ms /src/modules/command-menu/components/CommandMenuOpenContainer.tsx [linaria/wyw] ========================================== [2m[32m[39m[22m [7m[1m[32m NX [39m[22m[27m [32mSuccessfully ran target [1mbuild[22m for project twenty-front and [1m8[22m tasks it depends on[39m [2mNx read the output from the cache instead of running the command for 8 out of 9 tasks.[22m
testpassed · 219.6s
0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/captcha/utils/__tests__/[22m[1mgetCaptchaUrlByProvider.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/side-panel/pages/page-layout/utils/__tests__/[22m[1mgetSortLabelSuffixForFieldType.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/[22m[1mcombineFilters.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/utils/__tests__/[22m[1msleep.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/[22m[1mgetMaxLabelLength.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/localization/utils/detection/__tests__/[22m[1mdetectTimeFormat.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/ui/utilities/hotkey/hooks/__tests__/[22m[1museGoToHotkeys.test.tsx[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/workflow/workflow-trigger/utils/__tests__/[22m[1mgetTriggerIcon.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/object-record/record-table/empty-state/utils/__tests__/[22m[1mgetEmptyStateSubTitle.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/activities/files/utils/__tests__/[22m[1mdownloadFile.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/object-record/utils/__tests__/[22m[1mfilterAvailableTableColumns.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/command-menu/hooks/__tests__/[22m[1museCommandMenuOnItemClick.test.tsx[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/ui/utilities/drag-select/hooks/__tests__/[22m[1museDragSelect.test.tsx[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/http-request-action/utils/__tests__/[22m[1misMethodWithBody.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/settings/developers/utils/__tests__/[22m[1mcomputeNewExpirationDate.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/utils/__tests__/[22m[1mparseApolloStoreFieldName.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/object-record/utils/__tests__/[22m[1mgetUpdatedFieldsFromRecordInput.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/views/utils/__tests__/[22m[1mmapViewFilterGroupLogicalOperatorToRecordFilterGroupLogicalOperator.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/modules/views/utils/__tests__/[22m[1mmapRecordFilterGroupLogicalOperatorToViewFilterGroupLogicalOperator.test.ts[22m [0m[7m[1m[32m PASS [39m[22m[27m[0m [0m[7m[37m twenty-front [39m[27m[0m [2mpackages/twenty-front/src/utils/validation-schemas/__te
Ran 6/8/2026, 11:20:02 PM · yarn
Get the code
Pull this branch
gh pr checkout 13 --repo kamransiddiqui/twenty
git fetch https://github.com/kamransiddiqui/twenty.git gitgot/issue-21166 git checkout -b gitgot/issue-21166 FETCH_HEAD
Fork-mode request: the branch lives on kamransiddiqui/twenty (your fork), not twentyhq/twenty, so the commands above target the fork.
Drive locally
Paste this into your local coding agent (Claude Code, Cursor, etc.) to continue iterating with full repo context.
You are continuing work on a draft PR for twentyhq/twenty, addressing issue #21166. First, check out the branch with gitgot's first-pass change (run from a clone of the repo): gh pr checkout 13 --repo kamransiddiqui/twenty Then review the diff and: - run the project's lint/typecheck/test commands and fix anything broken - if the diff is wrong-headed, revert it and start fresh from the issue - keep the change tight; do not refactor unrelated code
- No comments on the PR yet. Use the form below to start the thread.