Connect to network services
Use fetch() for most HTTP requests, XMLHttpRequest when progress events are required, and WebSocket for persistent two-way communication. Every remote endpoint must match a domain declared in the manifest.
data-src=../_shared/prerequisites.md
Allow network domains
UXP blocks undeclared network access. Add every HTTP or WebSocket endpoint the plugin needs to requiredPermissions.network.
List the allowed domains in manifest.json:
{
"requiredPermissions": {
"network": {
"domains": [
"https://api.example.com",
"https://*.adobe.io"
]
}
}
}
Use a wildcard only when the plugin must reach several related hosts, such as "https://api.*.example.com". Requests to any unmatched domain fail with a permission error.
data-variant=warning
data-slots=text
Choose a network API
UXP supports three primary ways to perform network communication:
These APIs are available globally; no require() call is needed.
Send requests with fetch()
Use fetch() for promise-based JSON, text, and binary requests.
data-slots=heading, code
data-repeat=2
data-languages=JavaScript, JSON
index.js
async function getForecast() {
try {
const response = await fetch(
"https://api.weather.gov/gridpoints/MTR/99,82/forecast"
);
if (!response.ok) {
throw new Error(
`HTTP error ${response.status}: ${response.statusText}`
);
}
const data = await response.json();
console.log(
`Forecast: ${data.properties.periods[0].detailedForecast}`
);
} catch (error) {
console.error("Failed to fetch forecast:", error);
}
}
manifest.json
{
"requiredPermissions": {
"network": {
"domains": ["https://api.weather.gov"]
}
}
}
data-variant=info
data-slots=text
json(), text(), or blob() for the expected data type. Each method returns a Promise.Load a remote image
An image URL also requires its domain in the allowlist:
data-slots=heading, code
data-repeat=2
data-languages=HTML, JSON
index.html
<img src="https://picsum.photos/300/200" alt="A random image" />
manifest.json
{
"requiredPermissions": {
"network": {
"domains": ["https://picsum.photos/"]
}
}
}
The <img> tag works the same as in a browser, provided the remote domain is allow-listed.
Send a JSON request
Set the content type and serialize the body when an API expects a JSON POST request:
async function postUserData(user) {
try {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(user)
});
if (!response.ok) throw new Error(`Server error: ${response.status}`);
const result = await response.json();
console.log("User created:", result);
} catch (error) {
console.error("Failed to post user data:", error);
}
}
// Example usage
postUserData({ name: "Jamie", role: "Editor" });
data-variant=warning
data-slots=heading, text
TextDecoder is unavailable for decoding a response stream. Check the UXP tech stack before relying on browser-specific helpers.async function fetchStreamedData() {
const response = await fetch("https://api.example.com/stream");
const reader = response.body.getReader();
const decoder = new TextDecoder(); // Not available in UXP.
// ...
}
Track progress with XMLHttpRequest
Use the event-driven XMLHttpRequest API when an operation needs progress events or upload monitoring.
data-slots=heading, code
data-repeat=2
data-languages=JavaScript, JSON
index.js
function getForecastWithXHR() {
const xhr = new XMLHttpRequest();
xhr.open(
"GET",
"https://api.weather.gov/gridpoints/MTR/99,82/forecast"
);
xhr.responseType = "json";
xhr.onload = () => {
if (xhr.status === 200) {
console.log(
`Forecast: ${xhr.response.properties.periods[0].detailedForecast}`
);
} else {
console.error(`XHR failed with status ${xhr.status}`);
}
};
xhr.onerror = () => console.error("Network error occurred");
xhr.send();
}
manifest.json
{
"requiredPermissions": {
"network": {
"domains": ["https://api.weather.gov"]
}
}
}
Maintain a WebSocket connection
Use a WebSocket for persistent, bidirectional updates.
data-slots=heading, code
data-repeat=2
data-languages=JavaScript, JSON
index.js
let socket;
async function connectToServer() {
try {
if (socket) {
console.log("Disconnecting existing socket...");
socket.close();
socket = null;
return;
}
socket = new WebSocket(
"wss://javascript.info/article/websocket/demo/hello"
);
socket.onopen = () => {
console.log("WebSocket connection established");
socket.send("Hello from your UXP plugin!");
};
socket.onmessage = (event) => {
console.log(`Message from server: ${event.data}`);
};
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
socket.onclose = () => {
console.log("Connection closed");
socket = null;
};
} catch (error) {
console.error("Failed to connect via WebSocket:", error);
}
}
manifest.json
{
"requiredPermissions": {
"network": {
"domains": ["wss://javascript.info"]
}
}
}
data-variant=info
data-slots=heading,text
Handle errors and timeouts
Network calls can fail: the user may be offline, the endpoint may be down, or your permission list might be incomplete.
- Always wrap network calls in
try...catchblocks. - Use
response.okto detect HTTP errors. - Set reasonable timeouts for long operations.
- Log informative errors using
console.error().
async function safeFetch(url, options = {}, timeoutMs = 8000) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
console.error("Network request failed:", error);
throw error;
} finally {
clearTimeout(timeout);
}
}
Troubleshoot network requests
requiredPermissions.network.domains