apps
Reads the Salla apps installed on the store: the code each one wants on the page, and each app's public settings.
import { apps, AppSnippet, AppSnippetRow, AppSettings } from '@salla.sa/twilight-theme-engine/api/apps';In plain words
Merchants install apps from the Salla App Store, such as a review widget or a live chat. Those apps add small pieces of code (snippets) to the storefront. apps.snippets() returns what each installed app wants to add and where; apps.settings() returns the public settings each app reads.
The engine already fetches both before every page and injects the snippets for you. Read them yourself only to list or check the installed apps.
Signature
apps.snippets(): Promise<AppSnippet[]> // GET apps/snippets, timeout 2.5 s
apps.settings(): Promise<AppSettings> // GET apps/snippets/settings, timeout 2.5 s → { apps: data?.apps ?? {} }
apps.queries.snippets() // key ['apps', 'snippets'], staleTime Infinity, retry 0
apps.queries.settings() // key ['apps', 'settings'], staleTime Infinity, retry 0
interface AppSnippet {
service: string;
app_id: string;
app_slug: string;
status: string;
id?: string; // encoded app id: its settings bucket key
snippets: AppSnippetRow[];
}
interface AppSnippetRow {
place: string; // e.g. 'before_body'
content: string; // inline HTML; may be empty when url is set
url?: string | null; // the script file of a CDN row
id?: number; // CDN rows only
customer_parameters?: string[];
}
interface AppSettings { apps: Record<string, Record<string, unknown>> }Try it live
- before_body: a script file on the CDN
- before_body: inline HTML
- before_body: inline HTML
import { useQuery } from '@tanstack/react-query';
import { apps } from '@salla.sa/twilight-theme-engine/api/apps';
/** Lists the installed apps. The engine already injects their snippets: never render content yourself. */
export function InstalledApps() {
const { data = [] } = useQuery(apps.queries.snippets());
return (
<ul>
{data.map((app) => (
<li key={app.app_id}>
{app.app_slug} · {app.snippets.length} snippet(s)
</li>
))}
</ul>
);
}
Example
import { useQuery } from '@tanstack/react-query';
import { apps } from '@salla.sa/twilight-theme-engine/api/apps';
/** For example: hide the theme's own reviews block when a reviews app is installed. */
export function useHasApp(slug: string): boolean {
const { data = [] } = useQuery(apps.queries.snippets());
return data.some((app) => app.app_slug === slug && app.status === 'enabled');
}
How it behaves
The root loader awaits both queries with a
.catchthat logs and continues, so a slow or failing apps API costs the page its app tags, never the page.retry: 0keeps the 2.5 s timeout the whole budget during the server render.Both stay fresh forever (
staleTime: Infinity), so a component reading them answers from the cache the root loader filled.The engine
AppsSnippetscomponent injects the snippets, and re-queries the settings in the browser withretry: 2.AppSnippet.idis the encoded app id: the key of that app's bucket insettings.apps, and the scope its snippets bind to. It is absent on payloads older than the settings API.The raw functions throw on an HTTP error; only the root loader catches.
Gotchas
Never render
contentyourself: the engine already injects every snippet, and third-party scripts could run twice.A settings bucket is not always an object: on the demo store an app without settings has
[]. Checktypeof bucket === 'object' && !Array.isArray(bucket)before reading keys.
Related
Source and docs
- Engine source:
packages/theme-engine/src/api/apps.ts