Skip to content

Commit 5be8bc1

Browse files
committed
feat(home): auth-aware landing page navigation
- Redirect authenticated users from / to /workspace via middleware (?home param bypasses) - Show "Go to App" instead of "Log in / Get started" in navbar for authenticated users - Logo links to /?home for authenticated users to stay in marketing context - Settings "Home Page" button opens /?home - Handle isPending session state to prevent CTA button flash
1 parent 7b6149d commit 5be8bc1

File tree

4 files changed

+151
-80
lines changed

4 files changed

+151
-80
lines changed

apps/sim/app/(home)/components/navbar/navbar.tsx

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Image from 'next/image'
55
import Link from 'next/link'
66
import { GithubOutlineIcon } from '@/components/icons'
77
import { cn } from '@/lib/core/utils/cn'
8+
import { useSession } from '@/lib/auth/auth-client'
89
import {
910
BlogDropdown,
1011
type NavBlogPost,
@@ -40,6 +41,9 @@ interface NavbarProps {
4041

4142
export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps) {
4243
const brand = getBrandConfig()
44+
const { data: session, isPending: isSessionPending } = useSession()
45+
const isAuthenticated = Boolean(session?.user?.id)
46+
const logoHref = isAuthenticated ? '/?home' : '/'
4347
const [activeDropdown, setActiveDropdown] = useState<DropdownId>(null)
4448
const [hoveredLink, setHoveredLink] = useState<string | null>(null)
4549
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
@@ -92,7 +96,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
9296
itemScope
9397
itemType='https://schema.org/SiteNavigationElement'
9498
>
95-
<Link href='/' className={LOGO_CELL} aria-label={`${brand.name} home`} itemProp='url'>
99+
<Link href={logoHref} className={LOGO_CELL} aria-label={`${brand.name} home`} itemProp='url'>
96100
<span itemProp='name' className='sr-only'>
97101
{brand.name}
98102
</span>
@@ -121,7 +125,11 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
121125
{!logoOnly && (
122126
<>
123127
<ul className='mt-[0.75px] hidden lg:flex'>
124-
{NAV_LINKS.map(({ label, href, external, icon, dropdown }) => {
128+
{NAV_LINKS.map(({ label, href: rawHref, external, icon, dropdown }) => {
129+
const href =
130+
isAuthenticated && rawHref.startsWith('/#')
131+
? `/?home${rawHref.slice(1)}`
132+
: rawHref
125133
const hasDropdown = !!dropdown
126134
const isActive = hasDropdown && activeDropdown === dropdown
127135
const isThisHovered = hoveredLink === label
@@ -206,21 +214,38 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
206214

207215
<div className='hidden flex-1 lg:block' />
208216

209-
<div className='hidden items-center gap-[8px] pr-[80px] pl-[20px] lg:flex'>
210-
<Link
211-
href='/login'
212-
className='inline-flex h-[30px] items-center rounded-[5px] border border-[#3d3d3d] px-[9px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
213-
aria-label='Log in'
214-
>
215-
Log in
216-
</Link>
217-
<Link
218-
href='/signup'
219-
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
220-
aria-label='Get started with Sim'
221-
>
222-
Get started
223-
</Link>
217+
<div
218+
className={cn(
219+
'hidden items-center gap-[8px] pr-[80px] pl-[20px] lg:flex',
220+
isSessionPending && 'invisible'
221+
)}
222+
>
223+
{isAuthenticated ? (
224+
<Link
225+
href='/workspace'
226+
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
227+
aria-label='Go to app'
228+
>
229+
Go to App
230+
</Link>
231+
) : (
232+
<>
233+
<Link
234+
href='/login'
235+
className='inline-flex h-[30px] items-center rounded-[5px] border border-[#3d3d3d] px-[9px] text-[#ECECEC] text-[13.5px] transition-colors hover:bg-[#2A2A2A]'
236+
aria-label='Log in'
237+
>
238+
Log in
239+
</Link>
240+
<Link
241+
href='/signup'
242+
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
243+
aria-label='Get started with Sim'
244+
>
245+
Get started
246+
</Link>
247+
</>
248+
)}
224249
</div>
225250

226251
<div className='flex flex-1 items-center justify-end pr-[20px] lg:hidden'>
@@ -242,7 +267,12 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
242267
)}
243268
>
244269
<ul className='flex flex-col'>
245-
{NAV_LINKS.map(({ label, href, external }) => (
270+
{NAV_LINKS.map(({ label, href: rawHref, external }) => {
271+
const href =
272+
isAuthenticated && rawHref.startsWith('/#')
273+
? `/?home${rawHref.slice(1)}`
274+
: rawHref
275+
return (
246276
<li key={label} className='border-[#2A2A2A] border-b'>
247277
{external ? (
248278
<a
@@ -265,7 +295,8 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
265295
</Link>
266296
)}
267297
</li>
268-
))}
298+
)
299+
})}
269300
<li className='border-[#2A2A2A] border-b'>
270301
<a
271302
href='https://github.com/simstudioai/sim'
@@ -280,23 +311,36 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
280311
</li>
281312
</ul>
282313

283-
<div className='mt-auto flex flex-col gap-[10px] p-[20px]'>
284-
<Link
285-
href='/login'
286-
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#3d3d3d] text-[#ECECEC] text-[14px] transition-colors active:bg-[#2A2A2A]'
287-
onClick={() => setMobileMenuOpen(false)}
288-
aria-label='Log in'
289-
>
290-
Log in
291-
</Link>
292-
<Link
293-
href='/signup'
294-
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
295-
onClick={() => setMobileMenuOpen(false)}
296-
aria-label='Get started with Sim'
297-
>
298-
Get started
299-
</Link>
314+
<div className={cn('mt-auto flex flex-col gap-[10px] p-[20px]', isSessionPending && 'invisible')}>
315+
{isAuthenticated ? (
316+
<Link
317+
href='/workspace'
318+
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
319+
onClick={() => setMobileMenuOpen(false)}
320+
aria-label='Go to app'
321+
>
322+
Go to App
323+
</Link>
324+
) : (
325+
<>
326+
<Link
327+
href='/login'
328+
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#3d3d3d] text-[#ECECEC] text-[14px] transition-colors active:bg-[#2A2A2A]'
329+
onClick={() => setMobileMenuOpen(false)}
330+
aria-label='Log in'
331+
>
332+
Log in
333+
</Link>
334+
<Link
335+
href='/signup'
336+
className='flex h-[32px] items-center justify-center rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
337+
onClick={() => setMobileMenuOpen(false)}
338+
aria-label='Get started with Sim'
339+
>
340+
Get started
341+
</Link>
342+
</>
343+
)}
300344
</div>
301345
</div>
302346
</>

apps/sim/app/(landing)/components/nav/nav.tsx

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Link from 'next/link'
88
import { useRouter } from 'next/navigation'
99
import { GithubIcon } from '@/components/icons'
1010
import { isHosted } from '@/lib/core/config/feature-flags'
11+
import { useSession } from '@/lib/auth/auth-client'
1112
import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'
1213
import { useBrandConfig } from '@/ee/whitelabeling'
1314
import { useBrandedButtonClass } from '@/hooks/use-branded-button-class'
@@ -26,6 +27,9 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
2627
const router = useRouter()
2728
const brand = useBrandConfig()
2829
const buttonClass = useBrandedButtonClass()
30+
const { data: session, isPending: isSessionPending } = useSession()
31+
const isAuthenticated = Boolean(session?.user?.id)
32+
const logoHref = isAuthenticated ? '/?home' : '/?from=nav'
2933

3034
useEffect(() => {
3135
if (variant !== 'landing') return
@@ -72,7 +76,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
7276
</li>
7377
<li>
7478
<Link
75-
href='/?from=nav#pricing'
79+
href={isAuthenticated ? '/?home#pricing' : '/?from=nav#pricing'}
7680
className='text-[16px] text-muted-foreground transition-colors hover:text-foreground'
7781
scroll={true}
7882
>
@@ -124,7 +128,7 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
124128
itemType='https://schema.org/SiteNavigationElement'
125129
>
126130
<div className='flex items-center gap-[34px]'>
127-
<Link href='/?from=nav' aria-label={`${brand.name} home`} itemProp='url'>
131+
<Link href={logoHref} aria-label={`${brand.name} home`} itemProp='url'>
128132
<span itemProp='name' className='sr-only'>
129133
{brand.name} Home
130134
</span>
@@ -162,45 +166,68 @@ export default function Nav({ hideAuthButtons = false, variant = 'landing' }: Na
162166

163167
{/* Auth Buttons - show only when hosted, regardless of variant */}
164168
{!hideAuthButtons && isHosted && (
165-
<div className='flex items-center justify-center gap-[16px] pt-[1.5px]'>
166-
<button
167-
onClick={handleLoginClick}
168-
onMouseEnter={() => setIsLoginHovered(true)}
169-
onMouseLeave={() => setIsLoginHovered(false)}
170-
className='group hidden text-[#2E2E2E] text-[16px] transition-colors hover:text-foreground md:block'
171-
type='button'
172-
aria-label='Log in to your account'
173-
>
174-
<span className='flex items-center gap-1'>
175-
Log in
176-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
177-
{isLoginHovered ? (
178-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
179-
) : (
180-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
181-
)}
169+
<div className={`flex items-center justify-center gap-[16px] pt-[1.5px]${isSessionPending ? ' invisible' : ''}`}>
170+
{isAuthenticated ? (
171+
<Link
172+
href='/workspace'
173+
onMouseEnter={() => setIsHovered(true)}
174+
onMouseLeave={() => setIsHovered(false)}
175+
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
176+
aria-label='Go to app'
177+
>
178+
<span className='flex items-center gap-1'>
179+
Go to App
180+
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
181+
{isHovered ? (
182+
<ArrowRight className='h-4 w-4' aria-hidden='true' />
183+
) : (
184+
<ChevronRight className='h-4 w-4' aria-hidden='true' />
185+
)}
186+
</span>
182187
</span>
183-
</span>
184-
</button>
185-
<Link
186-
href='/signup'
187-
onMouseEnter={() => setIsHovered(true)}
188-
onMouseLeave={() => setIsHovered(false)}
189-
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
190-
aria-label='Get started with Sim - Sign up for free'
191-
prefetch={true}
192-
>
193-
<span className='flex items-center gap-1'>
194-
Get started
195-
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
196-
{isHovered ? (
197-
<ArrowRight className='h-4 w-4' aria-hidden='true' />
198-
) : (
199-
<ChevronRight className='h-4 w-4' aria-hidden='true' />
200-
)}
201-
</span>
202-
</span>
203-
</Link>
188+
</Link>
189+
) : (
190+
<>
191+
<button
192+
onClick={handleLoginClick}
193+
onMouseEnter={() => setIsLoginHovered(true)}
194+
onMouseLeave={() => setIsLoginHovered(false)}
195+
className='group hidden text-[#2E2E2E] text-[16px] transition-colors hover:text-foreground md:block'
196+
type='button'
197+
aria-label='Log in to your account'
198+
>
199+
<span className='flex items-center gap-1'>
200+
Log in
201+
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
202+
{isLoginHovered ? (
203+
<ArrowRight className='h-4 w-4' aria-hidden='true' />
204+
) : (
205+
<ChevronRight className='h-4 w-4' aria-hidden='true' />
206+
)}
207+
</span>
208+
</span>
209+
</button>
210+
<Link
211+
href='/signup'
212+
onMouseEnter={() => setIsHovered(true)}
213+
onMouseLeave={() => setIsHovered(false)}
214+
className={`${buttonClass} group inline-flex items-center justify-center gap-2 rounded-[10px] py-[6px] pr-[10px] pl-[12px] text-[15px] text-white transition-all`}
215+
aria-label='Get started with Sim - Sign up for free'
216+
prefetch={true}
217+
>
218+
<span className='flex items-center gap-1'>
219+
Get started
220+
<span className='inline-flex transition-transform duration-200 group-hover:translate-x-0.5'>
221+
{isHovered ? (
222+
<ArrowRight className='h-4 w-4' aria-hidden='true' />
223+
) : (
224+
<ChevronRight className='h-4 w-4' aria-hidden='true' />
225+
)}
226+
</span>
227+
</span>
228+
</Link>
229+
</>
230+
)}
204231
</div>
205232
)}
206233
</nav>

apps/sim/app/workspace/[workspaceId]/settings/components/general/general.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ export function General() {
461461
)}
462462
{isHosted && (
463463
<Button
464-
onClick={() => window.open('/?from=settings', '_blank', 'noopener,noreferrer')}
464+
onClick={() => window.open('/?home', '_blank', 'noopener,noreferrer')}
465465
variant='active'
466466
className='ml-auto'
467467
>

apps/sim/proxy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ function handleRootPathRedirects(
3636
}
3737

3838
// For root path, redirect authenticated users to workspace
39-
// Unless they have a 'from' query parameter (e.g., ?from=nav, ?from=settings)
39+
// Unless they have a 'home' query parameter (e.g., ?home)
4040
// This allows intentional navigation to the homepage from anywhere in the app
4141
if (hasActiveSession) {
42-
const from = url.searchParams.get('from')
43-
if (!from) {
42+
const isBrowsingHome = url.searchParams.has('home')
43+
if (!isBrowsingHome) {
4444
return NextResponse.redirect(new URL('/workspace', request.url))
4545
}
4646
}

0 commit comments

Comments
 (0)