Skip to main content
The OpenURLAction function creates an environment action that intercepts URL opening requests, allowing you to implement custom URL handling behavior instead of the default system behavior.
OpenURLAction(callback: (url: string) => OpenURLActionResult | void): Component

Parameters

  • callback - Function that receives a URL string and returns an OpenURLActionResult or void

Return Types

The callback can return one of these OpenURLActionResult types:

{ handled: true }

Indicates that the URL was handled by your custom logic and should not be processed further.

{ discarded: true }

Indicates that the URL opening request should be discarded/cancelled entirely.

{ systemAction: true }

Allows the system to handle the URL using default behavior.

{ systemAction: { url: string, preferInApp?: boolean } }

Allows the system to handle the URL, but with a potentially modified URL and optional in-app preference.

Usage

Basic URL interception

const body = () => {
  const handleURL = OpenURLAction((url) => {
    console.log(`Opening URL: ${url}`)

    // Let the system handle it
    return { systemAction: true }
  })

  return VStack([
    Text("Click links below"),
    Link("Visit Example", "https://example.com")
  ])
    .environment("openURL", handleURL)
}

Custom URL handling

const body = () => {
  const [message, setMessage] = useState("")

  const handleURL = OpenURLAction((url) => {
    if (url.startsWith("myapp://")) {
      // Handle custom scheme internally
      setMessage(`Internal navigation to: ${url}`)
      return { handled: true }
    }

    // Let system handle external URLs
    return { systemAction: true }
  })

  return VStack([
    Text(message),
    Link("Internal", "myapp://profile"),
    Link("External", "https://example.com")
  ])
    .environment("openURL", handleURL)
}

URL validation and blocking

const body = () => {
  const handleURL = OpenURLAction((url) => {
    // Block certain domains
    const blockedDomains = ["blocked-site.com", "malicious.com"]

    try {
      const urlObj = new URL(url)
      if (blockedDomains.includes(urlObj.hostname)) {
        console.warn(`Blocked access to: ${url}`)
        return { discarded: true }
      }
    } catch (e) {
      console.error("Invalid URL:", url)
      return { discarded: true }
    }

    return { systemAction: true }
  })

  return VStack([
    Text("Safe browsing enabled"),
    Link("Safe link", "https://example.com"),
    Link("Blocked link", "https://blocked-site.com")
  ])
    .environment("openURL", handleURL)
}

URL rewriting

const body = () => {
  const handleURL = OpenURLAction((url) => {
    // Rewrite HTTP to HTTPS
    if (url.startsWith("http://")) {
      const secureUrl = url.replace("http://", "https://")
      return {
        systemAction: {
          url: secureUrl,
          preferInApp: true
        }
      }
    }

    return { systemAction: true }
  })

  return VStack([
    Text("Automatic HTTPS upgrade"),
    Link("HTTP link", "http://example.com")
  ])
    .environment("openURL", handleURL)
}

Analytics tracking

const body = () => {
  const handleURL = OpenURLAction((url) => {
    // Track all outbound links
    analytics.track("link_clicked", {
      url: url,
      timestamp: Date.now()
    })

    // Allow system to handle normally
    return { systemAction: true }
  })

  return VStack([
    Text("Links are tracked"),
    Link("Tracked link 1", "https://example.com"),
    Link("Tracked link 2", "https://another-site.com")
  ])
    .environment("openURL", handleURL)
}

Conditional handling

const body = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false)

  const handleURL = OpenURLAction((url) => {
    // Require authentication for certain URLs
    if (url.includes("/premium/") && !isAuthenticated) {
      alert("Please log in to access premium content")
      return { discarded: true }
    }

    // Open external links in-app
    if (url.startsWith("https://")) {
      return {
        systemAction: {
          url: url,
          preferInApp: true
        }
      }
    }

    return { systemAction: true }
  })

  return VStack([
    Toggle({
      label: "Authenticated",
      isOn: isAuthenticated,
      setIsOn: setIsAuthenticated
    }),
    Link("Public content", "https://example.com/public"),
    Link("Premium content", "https://example.com/premium/article")
  ])
    .environment("openURL", handleURL)
}
const body = () => {
  const navigate = useNavigate()

  const handleURL = OpenURLAction((url) => {
    if (url.startsWith("myapp://")) {
      const path = url.replace("myapp://", "")

      // Route to different screens based on path
      if (path.startsWith("profile")) {
        navigate({ to: "/profile" })
      } else if (path.startsWith("settings")) {
        navigate({ to: "/settings" })
      } else if (path.startsWith("item/")) {
        const itemId = path.replace("item/", "")
        navigate({ to: "/item", props: { id: itemId } })
      }

      return { handled: true }
    }

    return { systemAction: true }
  })

  return VStack([
    Text("Deep link navigation"),
    Link("View Profile", "myapp://profile"),
    Link("Open Settings", "myapp://settings"),
    Link("View Item 123", "myapp://item/123")
  ])
    .environment("openURL", handleURL)
}

Notes

  • The action is passed to child components through the environment system
  • Child components can access it via useEnvironment().openURL(url, callback)
  • Returning void (no return value) is treated the same as { systemAction: true }
  • The preferInApp option suggests opening the URL in an in-app browser when available
  • Platform support for preferInApp varies - some platforms always open externally
  • URL validation is your responsibility - the system doesn’t validate URLs before calling your handler
  • For security, always validate and sanitize URLs before processing them
  • The handler is synchronous - use state updates or side effects for async operations
  • Custom schemes (like myapp://) are commonly used for deep linking
  • This is particularly useful for implementing navigation, analytics, and security policies