Implementation:Langgenius Dify UseAsyncWindowOpen
| Knowledge Sources | |
|---|---|
| Domains | Frontend, React_Hooks |
| Last Updated | 2026-02-12 07:00 GMT |
Overview
A React hook that opens a new browser window whose final URL is resolved asynchronously, working around popup-blocker restrictions by opening the window synchronously first.
Description
useAsyncWindowOpen returns a stable callback (memoized via useCallback) that handles the common pattern of needing to open a new browser tab whose destination URL must be fetched from an API. Because browsers block window.open calls that are not directly triggered by user gestures, the hook opens a window to about:blank immediately (within the user-gesture context) and then navigates it to the resolved URL once the asynchronous getUrl promise settles. If an immediateUrl is provided in the options, the window is opened directly to that URL instead, with noopener,noreferrer security features appended automatically. The hook gracefully handles failures: if the window cannot be opened, if the URL resolves to null, or if the promise rejects, the onError callback is invoked and the blank window is closed.
Usage
Use this hook when you need to open external URLs that are generated server-side, such as OAuth redirect URLs, payment checkout links, or download URLs that require an API call to produce. It is essential for maintaining popup-blocker compatibility with asynchronous URL resolution.
Code Reference
Source Location
- Repository: Langgenius_Dify
- File: web/hooks/use-async-window-open.ts
- Lines: 1-59
Signature
type GetUrl = () => Promise<string | null | undefined>
type AsyncWindowOpenOptions = {
immediateUrl?: string | null
target?: string
features?: string
onError?: (error: Error) => void
}
export const useAsyncWindowOpen = () => useCallback(
async (getUrl: GetUrl, options?: AsyncWindowOpenOptions) => void,
[]
)
Import
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| getUrl | null | undefined> | Yes (callback arg) | An async function that resolves to the target URL |
| options.immediateUrl | null | No | If provided, the window opens directly to this URL with security features |
| options.target | string | No (default: '_blank') | The window target name |
| options.features | string | No | Window features string passed to window.open |
| options.onError | (error: Error) => void | No | Error callback invoked when the window cannot be opened or the URL fails to resolve |
Outputs
| Name | Type | Description |
|---|---|---|
| openWindow | (getUrl: GetUrl, options?: AsyncWindowOpenOptions) => Promise<void> | Stable callback that opens a new window with the asynchronously resolved URL |
Usage Examples
Opening a Window with an Async URL
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
function ExportButton({ appId }: { appId: string }) {
const openWindow = useAsyncWindowOpen()
const handleClick = () => {
openWindow(
() => fetchExportUrl(appId),
{
target: '_blank',
onError: (err) => console.error('Failed to open export:', err),
},
)
}
return <button onClick={handleClick}>Export</button>
}
Opening with an Immediate URL
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
function OAuthButton() {
const openWindow = useAsyncWindowOpen()
const handleClick = () => {
openWindow(
() => Promise.resolve(null),
{
immediateUrl: 'https://provider.example.com/oauth/authorize?client_id=abc',
onError: (err) => console.error('OAuth window blocked:', err),
},
)
}
return <button onClick={handleClick}>Connect Account</button>
}