diff --git a/apps/web/package.json b/apps/web/package.json index c8526dac..cfd98f55 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -45,6 +45,7 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "react-icons": "^5.5.0", + "react-is": "^19.2.5", "recharts": "^3.6.0", "sonner": "^2.0.7", "tailwindcss": "^4.1.18", diff --git a/apps/web/src/components/landing/Landing.tsx b/apps/web/src/components/landing/Landing.tsx index 22155448..079ea5fd 100644 --- a/apps/web/src/components/landing/Landing.tsx +++ b/apps/web/src/components/landing/Landing.tsx @@ -11,7 +11,7 @@ import { LocalAccess } from "./local-access"; export const Landing = () => { return ( <> -
+
diff --git a/apps/web/src/components/landing/Terminal.tsx b/apps/web/src/components/landing/Terminal.tsx index d5ae330b..c5de9383 100644 --- a/apps/web/src/components/landing/Terminal.tsx +++ b/apps/web/src/components/landing/Terminal.tsx @@ -119,7 +119,7 @@ export function Terminal() { {logs.map((log, i) => (
{log.method} - {log.path} + {log.path} = 400 diff --git a/apps/web/src/components/landing/bring-your-own-domain.tsx b/apps/web/src/components/landing/bring-your-own-domain.tsx index a0d592b4..177fe25c 100644 --- a/apps/web/src/components/landing/bring-your-own-domain.tsx +++ b/apps/web/src/components/landing/bring-your-own-domain.tsx @@ -3,40 +3,40 @@ import { Check, Globe } from "lucide-react"; export const BringYourOwnDomain = () => (
-
+
-

+

Bring your
own domain

-

+

Don't want to use our random subdomains? No problem. Connect your own domain in seconds and get automatic SSL certificates.

-
+
- Automatic SSL/TLS certificates + Automatic SSL/TLS certificates
-
+
- Simple CNAME configuration + Simple CNAME configuration
-
+
- Zero-downtime reconfiguration + Zero-downtime reconfiguration
-
+
@@ -45,36 +45,36 @@ export const BringYourOwnDomain = () => (
-
-
-

+
+
+

api.yourcompany.com

- + Active
-

+

Added on {new Date().toLocaleDateString()}

-
-
+
+
Type CNAME
-
+
Name api
-
+
Value - edge.outray.app + edge.outray.app
-
+
TTL Auto
diff --git a/apps/web/src/components/landing/developer-experience.tsx b/apps/web/src/components/landing/developer-experience.tsx index 347a7ff7..df1caa52 100644 --- a/apps/web/src/components/landing/developer-experience.tsx +++ b/apps/web/src/components/landing/developer-experience.tsx @@ -9,54 +9,54 @@ const PluginTabs = () => { return (
-
+
-
+
-
-
-
+
+
+
- + {activeTab === "vite" ? "vite.config.ts" : activeTab === "next" ? "next.config.ts" : "index.js"}
-
+
{activeTab === "vite" ? (
               
@@ -101,7 +101,7 @@ const PluginTabs = () => {
 
       
         Learn more →
       
@@ -186,65 +186,65 @@ export const DeveloperExperience = () => {
 
   return (
     
-
-

+
+

First-class
developer experience

-
-
-
+
+
+
-
+
-

+

Online in one line

-

+

One command, you're online. Seriously, try for yourself.

-
+
$ outray 3000
-
+
-
+
-

Don't fw CLI?

+

Don't fw CLI?

-

+

Use our plugins to embed OutRay directly into your build tool.

-
+
-
+
-

+

Instant Observability

-

+

View live traffic happening on your APIs in real-time the second it goes online.

-
+
{visibleLogs.map((log) => ( { transition: { duration: 0.2 }, }} transition={{ duration: 0.4, type: "spring", bounce: 0 }} - className="flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-white/5" + className="flex items-center gap-2 sm:gap-3 p-2.5 sm:p-3 rounded-xl bg-white/5 border border-white/5" > - {log.status} - {log.method} - {log.path} - {log.time} + {log.status} + {log.method} + {log.path} + {log.time} ))} diff --git a/apps/web/src/components/landing/hero.tsx b/apps/web/src/components/landing/hero.tsx index 5aaa59e9..4fba9d88 100644 --- a/apps/web/src/components/landing/hero.tsx +++ b/apps/web/src/components/landing/hero.tsx @@ -50,25 +50,9 @@ export const Hero = () => {
-
-
-
- {/* posthog.capture("yc_badge_clicked")} - className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-white/5 border border-white/10 text-sm text-white/60 backdrop-blur-sm hover:bg-white/10 transition-colors" - initial={{ opacity: 0, y: -10 }} - animate={{ opacity: 1, y: 0 }} - transition={{ duration: 0.5 }} - > -
- - Y - -
- Not backed by Y Combinator -
*/} +
+
+
{ animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: 0.1 }} > - Vercel OSS Program + Vercel OSS Program
setExposeHovered(true)} onMouseLeave={() => setExposeHovered(false)} > - {/* Hidden streaming logs behind */} { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0 }} transition={{ duration: 0.3, type: "spring", bounce: 0 }} - className="flex items-center whitespace-nowrap h-4" + className="flex items-center whitespace-nowrap h-3 sm:h-4" > - {log.method} - {log.path} + {log.method} + {log.path} {log.status} ))} - {/* Front card that rotates */} { Expose {" "} - your local server
to the internet + your local server
to the internet
{
Get Started Free{" "} {
-

+

Unlimited{" "} .local {" "} domains

-

+

Test on any device in your network. Just add --local and your app is instantly accessible at a memorable .local domain.

-
+
-

mDNS Discovery

-

+

mDNS Discovery

+

Auto-discovered by all devices via Bonjour/Avahi

@@ -39,8 +39,8 @@ export const LocalAccess = () => (
-

Trusted HTTPS

-

+

Trusted HTTPS

+

Automatic certificates with mkcert - no browser warnings

@@ -50,8 +50,8 @@ export const LocalAccess = () => (
-

Zero Config

-

+

Zero Config

+

No port forwarding, no firewall rules, no DNS setup

@@ -59,7 +59,7 @@ export const LocalAccess = () => (
{/* Right - Terminal demo */} -
+
@@ -67,17 +67,17 @@ export const LocalAccess = () => (
-
+
$ outray 3000 --local
-
- ✨ Tunnel:{" "} - https://my-app.outray.app +
+ ✨ Tunnel: + https://my-app.outray.app
-
- 📡 Local:{" "} - https://my-app.local +
+ 📡 Local: + https://my-app.local
diff --git a/apps/web/src/components/landing/multiple-protocols.tsx b/apps/web/src/components/landing/multiple-protocols.tsx index 640f599e..a647e12c 100644 --- a/apps/web/src/components/landing/multiple-protocols.tsx +++ b/apps/web/src/components/landing/multiple-protocols.tsx @@ -4,11 +4,11 @@ export const MultipleProtocols = () => (
-

+

Any protocol,
anywhere.

-

+

OutRay isn't limited to just web traffic. Tunnel any TCP or UDP service securely to your local machine with a single command.

@@ -25,14 +25,14 @@ export const MultipleProtocols = () => (

HTTP/HTTPS

-

+

Instant secure URLs for your local web servers, webhooks, and APIs.

- outray http 3000 - + outray http 3000 +
@@ -47,13 +47,13 @@ export const MultipleProtocols = () => (

TCP Tunnels

-

+

Expose databases, SSH, RDP, and other TCP services securely.

- outray tcp 5432 - + outray tcp 5432 +
@@ -68,13 +68,13 @@ export const MultipleProtocols = () => (

UDP Tunnels

-

+

Perfect for game servers, streaming, and real-time applications.

- outray udp 25565 - + outray udp 25565 +
diff --git a/apps/web/src/components/landing/navbar.tsx b/apps/web/src/components/landing/navbar.tsx index 4802852c..1513a753 100644 --- a/apps/web/src/components/landing/navbar.tsx +++ b/apps/web/src/components/landing/navbar.tsx @@ -110,7 +110,7 @@ export const Navbar = () => { : "bg-transparent py-6" }`} > -
+
-

+

It just works

- + - +
-
+
-

+

100% Open Source

-

+

We believe in transparency and community. OutRay is built in the open, so you can inspect the code, contribute features, or self-host the entire platform on your own infrastructure. @@ -20,7 +20,7 @@ export function OpenSource() { href="https://github.com/akinloluwami/outray" target="_blank" rel="noopener noreferrer" - className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-white text-black font-medium hover:bg-gray-200 transition-colors" + className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-white text-black text-sm sm:text-base font-medium hover:bg-gray-200 transition-colors" > Star on GitHub @@ -29,7 +29,7 @@ export function OpenSource() { href="https://discord.gg/DncjGcCHDg" target="_blank" rel="noopener noreferrer" - className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-white/5 border border-white/10 text-white font-medium hover:bg-white/10 transition-colors" + className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-white/5 border border-white/10 text-white text-sm sm:text-base font-medium hover:bg-white/10 transition-colors" > Join Community @@ -37,18 +37,17 @@ export function OpenSource() {

-
-
-
+
+
-

+

Auditable Security

-

+

Verify our security claims by inspecting the source code yourself.

@@ -57,8 +56,8 @@ export function OpenSource() {
-

Community Driven

-

+

Community Driven

+

Built with feedback and contributions from developers like you.

@@ -67,8 +66,8 @@ export function OpenSource() {
-

Self-Hostable

-

+

Self-Hostable

+

Run the entire stack on your own servers. No vendor lock-in.

@@ -76,8 +75,8 @@ export function OpenSource() {
-

Free Forever

-

+

Free Forever

+

The core features will always be free and open source.

diff --git a/apps/web/src/components/overview/overview-card.tsx b/apps/web/src/components/overview/overview-card.tsx index 17129037..b106b5a2 100644 --- a/apps/web/src/components/overview/overview-card.tsx +++ b/apps/web/src/components/overview/overview-card.tsx @@ -29,19 +29,19 @@ export function OverviewCard({ : "text-red-400 bg-red-500/10 border-red-500/20"; return ( -
-
-
- {icon} +
+
+
+ {React.cloneElement(icon as React.ReactElement, { size: 18 })}
{(change !== undefined || trend !== "neutral") && (
{trend === "up" ? ( - + ) : trend === "down" ? ( - + ) : null} {showDash ? "-" @@ -53,17 +53,17 @@ export function OverviewCard({
-
+
{value}
-
+
{title}
{subValue && ( <> - {subValue} + {subValue} )}
diff --git a/apps/web/src/components/overview/overview-header.tsx b/apps/web/src/components/overview/overview-header.tsx index 8da51386..6a6522eb 100644 --- a/apps/web/src/components/overview/overview-header.tsx +++ b/apps/web/src/components/overview/overview-header.tsx @@ -9,7 +9,7 @@ export function OverviewHeader({ onNewTunnelClick: () => void; }) { return ( -
+

Overview @@ -22,9 +22,9 @@ export function OverviewHeader({ onClick={onNewTunnelClick} disabled={isAtLimit} leftIcon={} - className="shrink-0" + className="w-full sm:w-auto shrink-0" > - New Tunnel + New Tunnel

); diff --git a/apps/web/src/components/overview/request-activity-card.tsx b/apps/web/src/components/overview/request-activity-card.tsx index 36db3978..fa53a588 100644 --- a/apps/web/src/components/overview/request-activity-card.tsx +++ b/apps/web/src/components/overview/request-activity-card.tsx @@ -49,13 +49,13 @@ export function RequestActivityCard({ }); return ( -
+
{isPlaceholderData && (
)} -
+

@@ -63,12 +63,12 @@ export function RequestActivityCard({

Traffic over time

-
+
{["1h", "24h", "7d", "30d"].map((range) => (
{!chartData || chartData.length === 0 ? ( -
+

No traffic data available yet

) : ( -
+
@@ -106,14 +106,15 @@ export function RequestActivityCard({ `${value}`} diff --git a/apps/web/src/components/overview/stats-summary.tsx b/apps/web/src/components/overview/stats-summary.tsx index 1d54908a..a6317ead 100644 --- a/apps/web/src/components/overview/stats-summary.tsx +++ b/apps/web/src/components/overview/stats-summary.tsx @@ -13,7 +13,7 @@ export type OverviewStats = { export function StatsSummary({ stats }: { stats?: OverviewStats | null }) { return ( -
+
+
{/* Mobile header */} -
+
{/* Desktop sidebar - hidden on mobile */}
-
+
{/*
*/} -
+
diff --git a/apps/web/src/routes/pricing.tsx b/apps/web/src/routes/pricing.tsx index 310e8cee..c1e986ca 100644 --- a/apps/web/src/routes/pricing.tsx +++ b/apps/web/src/routes/pricing.tsx @@ -47,13 +47,13 @@ function PricingPage() {
-
+
-
-

+
+

Simple, transparent pricing

-

+

Start for free, upgrade as you grow. No hidden fees.

@@ -70,10 +70,10 @@ function PricingPage() { { value: "year" as const, label: ( - + Yearly - - 2 months free + + -2 months ), @@ -87,7 +87,7 @@ function PricingPage() {

-
+
{plans.map((plan) => { const f = plan.features as { maxTunnels: number; @@ -108,16 +108,16 @@ function PricingPage() { return (
{plan.id === "beam" && ( <>
-
+
Recommended @@ -125,31 +125,31 @@ function PricingPage() { )} -
+

{plan.name}

- + ${displayPriceUSD} - {intervalLabel} + {intervalLabel}
{showNGN && displayPriceNGN > 0 && (
- + ₦{displayPriceNGN.toLocaleString()} - + {intervalLabel}
)}
-
+
-