Back to blog
i18n with TanStack Start: A Complete Guide 2026
20 min read

i18n with TanStack Start: A Complete Guide 2026

Want a quick summary of this post? Tune in 🎧
Table of Contents

1. Introduction 🌍

Everyone hates doing i18n for apps, usually its way faster to just hardcode the strings in the code and call it a day, and thats totally valid depending on the project and how fast you need to get it done.

But specially today if you want to market your product to other countries that are not so “English speaking” friendly you might want to consider doing i18n, not only its a great benefit for your users, but also for your SEO and marketing. Even if you want to start just with English, having the strings in a single place might be a good idea to avoid duplication and keep your codebase clean.

BUT, this seems like a daunting task, and usually its a pain to implement in all frameworks that i have tried, specially with the routing logic.

2. Requirements 📋

You might think that this is a simple task right? Or that is probably a package that will just work out of the box? Well, yes and no. i18n can be tricky to implement in any framework, mostly because of the routing logic.

So here is what i usually want as a good i18n implementation:

  • Single source of truth for the strings for any language
  • Quickly add new languages with mostly zero effort.
  • NO prefix for the default language, and prefix for the other languages.
  • Ability to ignore certain routes to not be prefixed at all ( ex: /dashboard )
  • Small in Bundle size, and fast to load.
  • Support SSR, Async loading of the strings.
  • Ability to store in cookies or local storage, accept the language from the browser or from the user. Choose the best strategy for the job for your use case.

3. How frameworks handle locale prefixes 🛤️

Before diving into strategies, let’s see how different frameworks approach optional locale prefixes in routes.

Laravel handles this with route groups, but it’s not as simple as it looks:

Route::group(['prefix' => '{locale?}', 'where' => ['locale' => 'es|fr|de']], function () {
    Route::get('/about', [PageController::class, 'about']);
});

The {locale?} is optional and the where constraint ensures only valid locales match. But here’s the catch: /random-page would 404 because random-page doesn’t match the constraint—but you still need middleware to actually set the default locale when no prefix is provided, and you need to be careful with route ordering.

TanStack Router handles this with optional path parameters using the {-$param} syntax:

// app/routes/{-$locale}.tsx
export const Route = createFileRoute("/{-$locale}")({
  beforeLoad: ({ params }) => {
    const locale = params.locale;
    if (locale && !isValidLocale(locale)) {
      throw notFound();
    }
  },
  component: LocalizedLayout,
});

The - prefix makes the param optional. So /{-$locale}/about matches both /about (no locale) and /es/about (with locale). This is exactly what we need for section 4.3.

The key difference from Laravel: you need to validate in beforeLoad that the param is actually a valid locale, otherwise /random-page would be interpreted as a locale instead of a 404.

4. Prefixing or not to prefix? 🤔

This is one of the most important decisions you’ll make, and it affects SEO, user experience, and your routing complexity. There are typically three strategies:

4.1 Always prefix all languages (/en/about, /es/about)

This is the “easy” approach from a routing perspective—every language gets a prefix, no exceptions.

The problem? Your root / becomes useless. You need a redirect to /en (or detect the language and redirect). This means:

  • Extra redirect hop = slower initial load
  • Google sees / as a redirect, not your actual content
  • Your “main” URLs look ugly: example.com/en/pricing instead of example.com/pricing
  • Users sharing links always share the prefixed version

From an SEO perspective, Google handles redirects fine, but you’re essentially wasting your cleanest URL (/) on a 302/301 redirect instead of actual content.

4.2 Never prefix (detect via cookies/headers)

Clean URLs everywhere! Just /about for everyone, and you detect the language from:

  • Accept-Language header
  • A cookie from a previous visit
  • User’s account settings

Why this is terrible for SEO:

Google crawls your site without cookies and with Accept-Language: en. So Google only ever sees your English content. Your Spanish pages? They don’t exist to search engines. You can’t have proper hreflang tags pointing to different URLs because… there are no different URLs.

Also, CDN caching becomes a nightmare. Same URL, different content based on headers? You need Vary: Accept-Language headers and your cache hit rate drops significantly.

4.3 No prefix for default, prefix for others (/about, /es/about)

This is the sweet spot, but also the hardest to implement correctly:

  • /about → English (default, no prefix)
  • /es/about → Spanish
  • /fr/about → French

Why it’s hard:

Your router needs to handle two different URL patterns for the same page. In most frameworks, routes are defined once. Now you need:

/about           → AboutPage (lang: en)
/es/about        → AboutPage (lang: es)
/fr/about        → AboutPage (lang: fr)

This means your routing logic needs to:

  1. Check if the first segment is a known locale
  2. If yes, extract it and route to the rest of the path
  3. If no, assume default locale and route normally
  4. Handle links generation differently based on target language
  5. Not break when someone adds a page called /essays (is that a locale or a page?)

Most i18n libraries either don’t support this, or require you to duplicate every route definition. TanStack Router makes this manageable, but it’s still not trivial.

Why it’s worth it:

  • Your primary audience gets clean URLs
  • Full SEO support with proper hreflang tags
  • Each language has its own indexable URL
  • No redirect tax on your most important pages
  • CDN caching works perfectly (different URL = different cache entry)

This is the strategy we’ll implement in this guide.

5. Redirecting the user to the correct language 🔀

Once you got the previous section sorted out we will need to decide, what we do when the user first visits our website?

Let’s say a Spanish-speaking user lands on example.com/about (your English default). Should you:

  • Redirect them to /es/about?
  • Show English and let them switch manually?
  • Something in between?

There’s no universally correct answer. Let’s break down the options:

5.1 Redirect based on Accept-Language header

The browser sends an Accept-Language header with every request (e.g., es-ES,es;q=0.9,en;q=0.8). You can read this server-side and redirect accordingly.

Pros:

  • Feels magical—users see their language immediately
  • No user action required

Cons:

  • SEO disaster waiting to happen. Googlebot sends Accept-Language: en. If you redirect based on this header, Google might never index your default language pages properly. You could end up with Google indexing /es/about as your canonical when you wanted /about.
  • CDN caching breaks. Same URL, different redirects based on headers. You need Vary: Accept-Language which tanks your cache hit rate.
  • VPN and travel users get wrong language. Someone from Spain traveling in Germany might get German. Someone using a US VPN gets English even though they want Spanish.
  • Corporate/shared browsers lie. Many corporate machines are set to en-US regardless of the actual user.

The gotcha: If you MUST redirect based on Accept-Language, only do it on the FIRST visit and immediately set a cookie. Never redirect if a cookie already exists. And never, ever redirect if the user explicitly navigated to a prefixed URL—they chose that language.

Store the user’s language preference in a cookie when they:

  • Manually switch languages
  • Visit a prefixed URL (implicit preference)

On subsequent visits to non-prefixed URLs, redirect based on this cookie.

Pros:

  • Respects explicit user choice
  • Works great for returning visitors
  • No header-based guessing

Cons:

  • First-time visitors always see default language
  • Cookie might be stale (user changed preference on another device)
  • Still has CDN caching implications if you redirect server-side

The gotcha: Be careful with the cookie scope. If you set it on /es, it might not be readable on /. Always set cookies on the root path with path=/.

5.3 No redirect, just show the content

User lands on /about? Show English. User lands on /es/about? Show Spanish. No automatic redirects ever.

Pros:

  • SEO-perfect. Google sees exactly what you want it to see. No redirect chains, no confusion.
  • CDN-perfect. Every URL is deterministic. Cache everything aggressively.
  • Predictable. Users always get what the URL says.
  • Shareable links work correctly. When someone shares /about, everyone sees English regardless of their browser settings.

Cons:

  • First-time visitors might see the “wrong” language
  • Requires clear language switcher UI
  • Some users expect automatic detection

The gotcha: If you go this route, make your language switcher VERY visible. Don’t bury it in a footer dropdown. Consider a banner for detected language mismatch: “This page is available in Español →“

Here’s what I recommend for most production apps:

  1. Never redirect on page load based on headers alone
  2. Store preference in a cookie when user switches language or visits a prefixed URL
  3. On root / only (not other pages), consider a soft redirect for returning users with a cookie
  4. Show a non-intrusive language suggestion banner for first-time visitors when their Accept-Language doesn’t match the current page
┌─────────────────────────────────────────────────────────┐
│ 🌐 This page is available in Español. [Switch] [✕]     │
└─────────────────────────────────────────────────────────┘

This gives you:

  • Clean SEO (no redirects on content pages)
  • Good UX for returning visitors
  • Helpful hint for first-time visitors without being aggressive
  • Full CDN cacheability (banner can be client-side rendered)

5.5 What about localStorage?

Don’t use localStorage for language preference. It’s not accessible server-side, so you can’t use it for SSR. You’d end up with a flash of wrong language on every page load while the client-side code fixes it. Cookies are the way to go.

5.6 TL;DR - Quick comparison

StrategySEOCDN CacheUXComplexity
Accept-Language redirect❌ Bad❌ Bad✅ GreatMedium
Cookie redirect⚠️ OK⚠️ OK✅ GreatMedium
No redirect✅ Perfect✅ Perfect⚠️ OKLow
Hybrid✅ Great✅ Great✅ GreatHigh

My preference: No redirect on public pages. The URL dictates the language, period. But I still save the user’s preference to a cookie when they switch languages or visit a prefixed URL—this comes in handy for authenticated areas like /dashboard where SEO doesn’t matter and you can freely use the cookie to serve the right language without URL prefixes.

6. TanStack Start i18n Integration 🔧

Now that we’ve covered the theory—prefixing strategies, redirect approaches, and SEO considerations—let’s build the actual implementation.

Why a custom solution?

Most i18n libraries focus on translations: loading strings, pluralization, formatting. They assume you’ll figure out the routing yourself. The few that do handle routing often make assumptions that don’t match our requirements:

  • They want to prefix all languages (we don’t want /en)
  • They don’t support ignored paths (we need /dashboard without prefixes)
  • They don’t integrate well with TanStack Router’s rewrite system
  • They want to control the middleware layer in ways that conflict with TanStack Start

So we’re building a lightweight integration layer on top of use-intl. The translation library handles translations. We handle everything else: routing, URL rewriting, cookie management, and the glue between server and client.

Here’s what we’re building:

packages/i18n/
├── src/
│   ├── core/
│   │   ├── shared.ts    # Config, types, utilities (used by both)
│   │   ├── client.ts    # URL rewriting, locale detection
│   │   └── server.ts    # Middleware, cookie handling
│   ├── messages/
│   │   ├── en.ts        # English translations
│   │   ├── pt.ts        # Portuguese translations
│   │   └── index.ts     # Message registry
│   ├── entry.client.ts  # Client-side exports
│   └── entry.server.ts  # Server-side exports

Let’s break it down piece by piece.

6.1 Why a Separate Package?

Before we dive into code, let’s talk about project structure. In our monorepo, we keep i18n in a separate package: packages/i18n.

Why not just a folder in the app?

Server and client need completely different code paths:

  • Server code imports @tanstack/react-start/server (Node-only APIs)
  • Client code uses window.location, document.cookie
  • Mixing them causes bundler errors or bloated client bundles

By creating a package with separate entry points, we keep things clean:

{
  "name": "@app/i18n",
  "exports": {
    "./client": "./src/entry.client.ts",
    "./server": "./src/entry.server.ts",
    "./messages": "./src/messages/index.ts"
  }
}

Now the app imports exactly what it needs:

// In client code
import { getCurrentLocale, localizeUrl } from '@app/i18n/client'

// In server code (middleware)
import { handleLocaleMiddleware } from '@app/i18n/server'

The bundler never pulls server code into the client bundle. No runtime errors, no bloat.

6.2 Why use-intl?

There are many i18n libraries out there: i18next, react-intl, lingui, paraglide. We went with use-intl for a few reasons:

  • Lightweight — ~2kb gzipped, minimal footprint
  • TypeScript-first — Full type inference for translation keys
  • SSR-friendly — No hydration mismatches when used correctly
  • Simple API — Just useTranslations() and you’re done
import { useTranslations } from 'use-intl'

function SignInPage() {
  const t = useTranslations('auth')

  return (
    <div>
      <h1>{t('signIn')}</h1>
      <p>{t('noAccount')}</p>
    </div>
  )
}

The messages are plain objects, easy to maintain:

const en = {
  auth: {
    signIn: 'Sign In',
    signUp: 'Sign Up',
    noAccount: "Don't have an account?",
  },
  common: {
    loading: 'Loading...',
    error: 'An error occurred',
  },
}

Nothing fancy, nothing magical. Just objects and functions.

6.3 Why Not Paraglide?

Paraglide.js is genuinely impressive. Compile-time extraction, tiny runtime, great DX. So why didn’t we use it?

The integration felt off for our setup:

  • Middleware control is obscure — Paraglide wants to own the routing layer. Integrating with TanStack Start’s createServerEntry and our custom redirect logic felt like fighting the framework rather than working with it.
  • Routing assumptions don’t match — Paraglide pushes toward patterns that didn’t align with our “no prefix for default + ignored dashboard routes” strategy. Getting /dashboard to read from cookie while /about reads from URL required workarounds.
  • Debugging generated code — When something breaks in routing, you’re digging through compiler-generated files. With use-intl, everything is explicit and traceable.

We preferred something more mainstream. use-intl (from the next-intl author) has a larger community, more Stack Overflow answers, and patterns that translate across frameworks.

When Paraglide makes sense:

  • You’re using SvelteKit or Astro with first-class Paraglide support
  • You want automatic string extraction from code
  • You’re okay with always-prefix strategy or simpler routing needs

For TanStack Start with complex routing requirements, rolling our own with use-intl gave us full control.

6.4 The Shared Foundation

Everything starts with shared.ts—the config and utilities used by both server and client.

// packages/i18n/src/core/shared.ts

export const defaultLocale = 'en'
export const supportedLocales = ['en', 'pt'] as const
export const LOCALE_COOKIE = 'locale'

// Paths that bypass locale handling entirely
export const ignoredPathsRegex = /^\/(?:api|rpc|dashboard)(?:\/|$)/

export type Locale = (typeof supportedLocales)[number]

export function isValidLocale(locale: string | undefined): locale is Locale {
  return supportedLocales.includes(locale as Locale)
}

export function shouldIgnorePath(pathname: string): boolean {
  return ignoredPathsRegex.test(pathname)
}

export function extractLocaleFromPath(pathname: string): Locale | null {
  const match = /^\/([a-z]{2})(?:\/|$)/.exec(pathname)
  const locale = match?.[1]
  return locale && isValidLocale(locale) && locale !== defaultLocale ? locale : null
}

Why ignoredPathsRegex?

Some routes shouldn’t have locale prefixes:

  • /api/* — API endpoints don’t need localization in the URL
  • /rpc/* — Same for RPC handlers
  • /dashboard/* — Authenticated area, SEO doesn’t matter, reads from cookie

This regex lets us skip locale handling for these paths entirely.

6.5 TanStack Router URL Rewriting

Here’s where the magic happens. TanStack Router has a rewrite option that transforms URLs:

// apps/web/src/application/router.tsx
import { deLocalizeUrl, localizeUrl } from '@app/i18n/client'

const router = createTanStackRouter({
  routeTree,
  rewrite: {
    input: ({ url }) => deLocalizeUrl(url),
    output: ({ url }) => localizeUrl(url),
  },
})

What does this do?

  • input (deLocalizeUrl): Strips the locale prefix before routing. User visits /es/about, router sees /about.
  • output (localizeUrl): Adds the locale prefix when generating links. <Link to="/about"> becomes /es/about.

Why do we need this?

Without rewriting, you’d have two problems:

  1. Every route would need to handle both /about and /es/about explicitly
  2. All your <Link> components would generate wrong URLs

The rewrite functions:

export function deLocalizeUrl(url: URL): URL {
  if (shouldIgnorePath(url.pathname)) return url

  const locale = extractLocaleFromPath(url.pathname)
  if (locale) {
    const newUrl = new URL(url)
    newUrl.pathname = url.pathname.replace(`/${locale}`, '') || '/'
    return newUrl
  }
  return url
}

export function localizeUrl(url: URL): URL {
  if (shouldIgnorePath(url.pathname)) return url

  const locale = getCurrentLocale()
  if (locale === defaultLocale) return url

  const newUrl = new URL(url)
  newUrl.pathname = `/${locale}${url.pathname === '/' ? '' : url.pathname}`
  return newUrl
}

Notice how both functions respect shouldIgnorePath(). Links to /dashboard stay as /dashboard, never /es/dashboard.

6.6 The Layout Route

The /{-$locale} layout route is where we validate the locale param:

// apps/web/src/routes/localized.layout.tsx
import { isValidLocale } from '@app/i18n/client'
import { createFileRoute, notFound, Outlet } from '@tanstack/react-router'

export const Route = createFileRoute('/{-$locale}')({
  beforeLoad: ({ params }) => {
    const locale = params.locale
    if (locale && !isValidLocale(locale)) {
      throw notFound()
    }
  },
  component: LocalizedLayout,
})

function LocalizedLayout() {
  return <Outlet />
}

Why do we need both this AND server middleware?

They handle different things:

ConcernLayout RouteServer Middleware
Validate /xyz/about → 404
Redirect /en/about → /about
Sync cookie from URL
Strip locale from /es/dashboard

The layout route runs inside React, after routing. It can throw notFound() but can’t do redirects before HTML is sent. Server middleware runs before React, so it handles redirects and cookie syncing.

6.7 Server Middleware

The server middleware handles everything that must happen before React renders:

// packages/i18n/src/core/server.ts

export function handleLocaleMiddleware(request: Request): LocaleMiddlewareResult {
  const url = new URL(request.url)
  const pathname = url.pathname

  // Skip for ignored paths
  if (shouldIgnorePath(pathname)) {
    return {}
  }

  // Redirect /en/* to /* (default locale shouldn't have prefix)
  if (pathname.startsWith(`/${defaultLocale}/`) || pathname === `/${defaultLocale}`) {
    url.pathname = pathname.replace(`/${defaultLocale}`, '') || '/'
    return { redirect: Response.redirect(url.toString(), 301) }
  }

  const urlLocale = extractLocaleFromPath(pathname)

  if (urlLocale) {
    // Strip locale from ignored paths: /es/dashboard → /dashboard
    const strippedPath = pathname.replace(`/${urlLocale}`, '') || '/'
    if (shouldIgnorePath(strippedPath)) {
      url.pathname = strippedPath
      return { redirect: Response.redirect(url.toString(), 301) }
    }

    // Sync cookie when URL has explicit locale
    const cookieLocale = parseLocaleCookie(request.headers.get('cookie'))
    if (urlLocale !== cookieLocale) {
      return { setCookie: { name: LOCALE_COOKIE, value: urlLocale } }
    }
  }

  return {}
}

Integration with TanStack Start:

// apps/web/src/application/server.ts
import { handleLocaleMiddleware, createCookieHeader } from '@app/i18n/server'
import handler, { createServerEntry } from '@tanstack/react-start/server-entry'

export default createServerEntry({
  async fetch(request) {
    const { redirect, setCookie } = handleLocaleMiddleware(request)

    if (redirect) {
      return redirect
    }

    const response = await handler.fetch(request)

    if (setCookie) {
      const cookieValue = createCookieHeader(setCookie.name, setCookie.value)
      response.headers.append('Set-Cookie', cookieValue)
    }

    return response
  },
})

What this handles:

  1. /en/about → 301 redirect to /about
  2. /es/dashboard → 301 redirect to /dashboard (ignored path)
  3. /es/about → Sets cookie to es if not already set
  4. /about → No action needed

6.8 Client-Side Locale Detection

Getting the current locale needs to work identically on server (SSR) and client. We use TanStack’s createIsomorphicFn():

// packages/i18n/src/core/client.ts
import { getRequest } from '@tanstack/react-start/server'
import { createIsomorphicFn } from '@tanstack/react-start'

export const getCurrentLocale = createIsomorphicFn()
  .server(() => {
    const request = getRequest()
    const url = new URL(request.url)

    // Dashboard reads from cookie
    if (shouldIgnorePath(url.pathname)) {
      return parseLocaleCookie(request.headers.get('cookie')) ?? defaultLocale
    }
    // Public pages read from URL
    return extractLocaleFromPath(url.pathname) ?? defaultLocale
  })
  .client(() => {
    // Dashboard reads from cookie
    if (shouldIgnorePath(window.location.pathname)) {
      return parseLocaleCookie(document.cookie) ?? defaultLocale
    }
    // Public pages read from URL
    return extractLocaleFromPath(window.location.pathname) ?? defaultLocale
  })

Why the split logic?

  • Public pages (/about, /es/about): Locale comes from URL. This is the source of truth for SEO.
  • Dashboard (/dashboard): No locale in URL, so we read from cookie. User’s preference persists across the authenticated area.

This is how we satisfy our original requirement: public pages use URL prefixes for SEO, but dashboard ignores prefixes and uses cookies.

6.9 The Provider Setup

The IntlProvider wraps the app and provides translations via React context:

// apps/web/src/components/context/context.providers.tsx
import { IntlProvider, getClientLocale } from '@app/i18n/client'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useRouterState } from '@tanstack/react-router'

export function Providers({ children }: { children: ReactNode }) {
  const routerPathname = useRouterState({ select: (s) => s.location.pathname })
  const locale = getClientLocale(routerPathname)

  const { data: messages } = useSuspenseQuery(
    rpc.i18n.messages.queryOptions({
      input: locale,
      staleTime: Infinity,
      gcTime: Infinity,
    })
  )

  return (
    <IntlProvider locale={locale} messages={messages}>
      {children}
    </IntlProvider>
  )
}

Why useRouterState?

The provider needs to react to route changes. When navigating from /about to /es/about, the locale changes and we need to fetch Spanish messages.

Why async loading via RPC?

Messages are loaded on-demand per locale. English users never download Spanish strings. With staleTime: Infinity, messages are cached forever (until page refresh).

Setting the lang attribute:

// apps/web/src/routes/root.tsx
function RootDocument({ children }: { children: ReactNode }) {
  const locale = getCurrentLocale()
  return (
    <html lang={locale}>
      <head><HeadContent /></head>
      <body>{children}</body>
    </html>
  )
}

Regular <Link to="/about"> works but loses type safety for localized routes. We created a LocalizedLink component:

// apps/web/src/components/misc/localized-link.tsx
import { Link } from '@tanstack/react-router'
import type { FileRouteTypes } from '@/application/routes.tree'

type LocalizedFullPaths = Extract<FileRouteTypes['to'], `/{-$locale}${string}`>

type StripLocalePrefix<T extends string> = T extends '/{-$locale}'
  ? '/'
  : T extends `/{-$locale}${infer Rest}`
  ? Rest
  : never

export type LocalizedTo = StripLocalePrefix<LocalizedFullPaths>

export function LocalizedLink<TTo extends LocalizedTo>({
  to,
  ...props
}: LocalizedLinkProps<TTo>) {
  const fullPath = to === '/' ? '/{-$locale}' : `/{-$locale}${to}`
  return <Link to={fullPath} {...props} />
}

Usage:

// Type-safe! Only accepts routes that exist under /{-$locale}/*
<LocalizedLink to="/auth/sign-up">Sign up</LocalizedLink>

// TypeScript error: "/invalid-route" doesn't exist
<LocalizedLink to="/invalid-route">Nope</LocalizedLink>

The router’s rewrite.output handles adding the actual locale prefix. You write /auth/sign-up, users see /es/auth/sign-up.

6.11 Adding a New Language

With everything in place, adding a new language is trivial:

1. Create the message file:

// packages/i18n/src/messages/es.ts
const es = {
  auth: {
    signIn: 'Iniciar Sesión',
    signUp: 'Registrarse',
    noAccount: '¿No tienes una cuenta?',
  },
  common: {
    loading: 'Cargando...',
    error: 'Ocurrió un error',
  },
}

export default es

2. Add to supported locales:

// packages/i18n/src/core/shared.ts
export const supportedLocales = ['en', 'pt', 'es'] as const

3. Register in messages index:

// packages/i18n/src/messages/index.ts
import en from './en'
import pt from './pt'
import es from './es'

export const messages: Record<Locale, Messages> = { en, pt, es }

That’s it. No route changes, no middleware updates, no config files. The routing, links, and cookie handling all just work.

7. Wrapping Up 🎉

We built a complete i18n solution for TanStack Start that:

  • ✅ No prefix for default language (/about)
  • ✅ Prefix for other languages (/es/about)
  • ✅ Ignored paths read from cookie (/dashboard)
  • ✅ SEO-friendly with proper URLs per language
  • ✅ Type-safe translations and links
  • ✅ SSR-compatible with no hydration issues
  • ✅ Easy to add new languages

The key insight: i18n isn’t just about translations. It’s about routing, redirects, cookies, and making all these pieces work together. By building on use-intl and TanStack Router‘s rewrite system, we get a solution that’s both flexible and maintainable. Now its time to get that SEO score up!

Like this post? Sharing it means a lot to me! ❤️