diff --git a/frontend/src/components/layouts/AppLayout.tsx b/frontend/src/components/layouts/AppLayout.tsx
index beced9ff3..a6b06cac1 100644
--- a/frontend/src/components/layouts/AppLayout.tsx
+++ b/frontend/src/components/layouts/AppLayout.tsx
@@ -9,6 +9,7 @@ import {
useCommandPaletteContext,
} from "src/contexts/CommandPaletteContext";
import { useBanner } from "src/hooks/useBanner";
+import { useDocumentTitle } from "src/hooks/useDocumentTitle";
import { useInfo } from "src/hooks/useInfo";
import { useNotifyReceivedPayments } from "src/hooks/useNotifyReceivedPayments";
import { useRemoveSuccessfulChannelOrder } from "src/hooks/useRemoveSuccessfulChannelOrder";
@@ -21,6 +22,7 @@ function AppLayoutInner() {
useRemoveSuccessfulChannelOrder();
useNotifyReceivedPayments();
+ useDocumentTitle();
if (!info) {
return null;
diff --git a/frontend/src/hooks/useDocumentTitle.ts b/frontend/src/hooks/useDocumentTitle.ts
new file mode 100644
index 000000000..7349cd340
--- /dev/null
+++ b/frontend/src/hooks/useDocumentTitle.ts
@@ -0,0 +1,49 @@
+import React from "react";
+import { useMatches } from "react-router-dom";
+
+/**
+ * Custom hook to manage document.title based on route handles.
+ * This ensures the browser's history entries include a proper title
+ * (fixes: back gesture / long-press back showing empty/incorrect titles).
+ *
+ * Looks for a `title` property in route handles (string or function),
+ * falling back to "Alby Hub" if not present.
+ */
+export function useDocumentTitle() {
+ const matches = useMatches();
+
+ React.useEffect(() => {
+ try {
+ // Extract title from route handles. Use a typed helper to get the title
+ // from the handle object (if present).
+ const getTitleFromHandle = (handle: unknown): string | null => {
+ if (handle && typeof handle === "object") {
+ const h = handle as { title?: unknown };
+ if (typeof h.title === "string") {
+ return h.title;
+ }
+ if (typeof h.title === "function") {
+ try {
+ return (h.title as () => string)();
+ } catch (err) {
+ return null;
+ }
+ }
+ }
+ return null;
+ };
+
+ // Find the last (most specific) route with a title, or default to "Alby Hub"
+ const routeTitle =
+ matches
+ .map((m) => getTitleFromHandle(m.handle))
+ .filter(Boolean)
+ .pop() || "Alby Hub";
+
+ // Set document title
+ document.title = routeTitle;
+ } catch (err) {
+ console.error("Failed to compute page title", err);
+ }
+ }, [matches]);
+}
diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx
index 5ed1436a1..57e2faef9 100644
--- a/frontend/src/routes.tsx
+++ b/frontend/src/routes.tsx
@@ -102,7 +102,7 @@ const routes = [
{
path: "home",
element: ,
- handle: { crumb: () => "Dashboard" },
+ handle: { crumb: () => "Dashboard", title: "Dashboard" },
children: [
{
index: true,
@@ -113,7 +113,7 @@ const routes = [
{
path: "wallet",
element: ,
- handle: { crumb: () => "Wallet" },
+ handle: { crumb: () => "Wallet", title: "Wallet" },
children: [
{
index: true,
@@ -121,7 +121,7 @@ const routes = [
},
{
path: "swap",
- handle: { crumb: () => "Swap" },
+ handle: { crumb: () => "Swap", title: "Swap" },
children: [
{
index: true,
@@ -143,24 +143,30 @@ const routes = [
},
{
path: "receive",
- handle: { crumb: () => "Receive" },
+ handle: { crumb: () => "Receive", title: "Receive" },
children: [
{
index: true,
element: ,
},
{
- handle: { crumb: () => "Receive On-chain" },
+ handle: {
+ crumb: () => "Receive On-chain",
+ title: "Receive On-chain",
+ },
path: "onchain",
element: ,
},
{
- handle: { crumb: () => "Invoice" },
+ handle: { crumb: () => "Invoice", title: "Invoice" },
path: "invoice",
element: ,
},
{
- handle: { crumb: () => "BOLT-12 Offer" },
+ handle: {
+ crumb: () => "BOLT-12 Offer",
+ title: "BOLT-12 Offer",
+ },
path: "offer",
element: ,
},
@@ -168,7 +174,7 @@ const routes = [
},
{
path: "send",
- handle: { crumb: () => "Send" },
+ handle: { crumb: () => "Send", title: "Send" },
children: [
{
index: true,
@@ -203,24 +209,27 @@ const routes = [
{
path: "sign-message",
element: ,
- handle: { crumb: () => "Sign Message" },
+ handle: { crumb: () => "Sign Message", title: "Sign Message" },
},
{
path: "node-alias",
element: ,
- handle: { crumb: () => "Node Alias" },
+ handle: { crumb: () => "Node Alias", title: "Node Alias" },
},
{
path: "withdraw",
element: ,
- handle: { crumb: () => "Withdraw On-Chain Balance" },
+ handle: {
+ crumb: () => "Withdraw On-Chain Balance",
+ title: "Withdraw On-Chain Balance",
+ },
},
],
},
{
path: "settings",
element: ,
- handle: { crumb: () => "Settings" },
+ handle: { crumb: () => "Settings", title: "Settings" },
children: [
{
path: "",
@@ -233,22 +242,25 @@ const routes = [
{
path: "about",
element: ,
- handle: { crumb: () => "About" },
+ handle: { crumb: () => "About", title: "About" },
},
{
path: "auto-unlock",
element: ,
- handle: { crumb: () => "Auto Unlock" },
+ handle: { crumb: () => "Auto Unlock", title: "Auto Unlock" },
},
{
path: "change-unlock-password",
element: ,
- handle: { crumb: () => "Unlock Password" },
+ handle: {
+ crumb: () => "Unlock Password",
+ title: "Change Unlock Password",
+ },
},
{
path: "backup",
element: ,
- handle: { crumb: () => "Backup" },
+ handle: { crumb: () => "Backup", title: "Backup" },
},
{
path: "node-migrate",
@@ -273,7 +285,7 @@ const routes = [
{
path: "apps",
element: ,
- handle: { crumb: () => "Connections" },
+ handle: { crumb: () => "Connections", title: "Connections" },
children: [
{
index: true,
@@ -286,7 +298,7 @@ const routes = [
{
path: "new",
element: ,
- handle: { crumb: () => "New App" },
+ handle: { crumb: () => "New App", title: "New Connection" },
},
{
path: "cleanup",
@@ -297,7 +309,7 @@ const routes = [
{
path: "sub-wallets",
element: ,
- handle: { crumb: () => "Sub-wallets" },
+ handle: { crumb: () => "Sub-wallets", title: "Sub-wallets" },
children: [
{
@@ -317,7 +329,7 @@ const routes = [
{
path: "internal-apps",
element: ,
- handle: { crumb: () => "Connections" },
+ handle: { crumb: () => "Connections", title: "Internal Apps" },
children: [
{
path: "buzzpay",
@@ -356,7 +368,7 @@ const routes = [
{
path: "appstore",
element: ,
- handle: { crumb: () => "App Store" },
+ handle: { crumb: () => "App Store", title: "App Store" },
children: [
{
path: ":appStoreId",
@@ -367,7 +379,7 @@ const routes = [
{
path: "channels",
element: ,
- handle: { crumb: () => "Node" },
+ handle: { crumb: () => "Node", title: "Channels" },
children: [
{
index: true,
@@ -375,7 +387,10 @@ const routes = [
},
{
path: "first",
- handle: { crumb: () => "Your First Channel" },
+ handle: {
+ crumb: () => "Your First Channel",
+ title: "Your First Channel",
+ },
children: [
{
index: true,
@@ -393,7 +408,7 @@ const routes = [
},
{
path: "auto",
- handle: { crumb: () => "New Channel" },
+ handle: { crumb: () => "New Channel", title: "New Channel" },
children: [
{
index: true,
@@ -412,34 +427,43 @@ const routes = [
{
path: "outgoing",
element: ,
- handle: { crumb: () => "Open Channel with On-Chain" },
+ handle: {
+ crumb: () => "Open Channel with On-Chain",
+ title: "Open Channel with On-Chain",
+ },
},
{
path: "incoming",
element: ,
- handle: { crumb: () => "Open Channel with Lightning" },
+ handle: {
+ crumb: () => "Open Channel with Lightning",
+ title: "Open Channel with Lightning",
+ },
},
{
path: "order",
element: ,
- handle: { crumb: () => "Current Order" },
+ handle: { crumb: () => "Current Order", title: "Current Order" },
},
{
path: "onchain/buy-bitcoin",
element: ,
- handle: { crumb: () => "Buy Bitcoin" },
+ handle: { crumb: () => "Buy Bitcoin", title: "Buy Bitcoin" },
},
{
path: "onchain/deposit-bitcoin",
element: ,
- handle: { crumb: () => "Deposit Bitcoin" },
+ handle: {
+ crumb: () => "Deposit Bitcoin",
+ title: "Deposit Bitcoin",
+ },
},
],
},
{
path: "peers",
element: ,
- handle: { crumb: () => "Peers" },
+ handle: { crumb: () => "Peers", title: "Peers" },
children: [
{
index: true,
@@ -448,7 +472,7 @@ const routes = [
{
path: "new",
element: ,
- handle: { crumb: () => "Connect Peer" },
+ handle: { crumb: () => "Connect Peer", title: "Connect Peer" },
},
],
},
@@ -459,7 +483,7 @@ const routes = [
{
path: "review-earn",
element: ,
- handle: { crumb: () => "Review & Earn" },
+ handle: { crumb: () => "Review & Earn", title: "Review & Earn" },
},
],
},