Freedam Picker Quickstart
This page adds a Choose from Freedam button to a page of your application. A click opens the Freedam Picker in a window; the person chooses an image, and your page receives its URL, its alternative text and its credit.
Every option, event and payload field is described in the Freedam Picker reference. To try the options before writing code, open the playground: it opens the picker on a demo workspace, shows the code for the options you choose and the payload your page would receive.
Before you start
Ask a workspace administrator to create an integration on the Picker integrations page and to add your page's origin to it, for example https://cms.example.com. You need two values from them:
- the workspace host, for example
acme.freedam.io; - the integration key, for example
w5r8n2c4t7bd.
People who use the button need a Freedam account in the workspace. Those who are not signed in sign in from the picker window, in a second window that closes itself afterwards. If the integration allows access without an account, the administrator also gives you a public key; pass it as publicKey and visitors without an account can choose assets too.
A complete page
Save this page on your origin, replace the workspace host and the integration key, and open it:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Freedam picker</title>
<script src="https://acme.freedam.io/sdk/picker/v1/picker.js"></script>
</head>
<body>
<button type="button" id="choose">Choose from Freedam</button>
<figure id="result" hidden>
<img id="image" alt="" />
<figcaption id="credit"></figcaption>
</figure>
<script>
document.getElementById('choose').addEventListener('click', async () => {
// No await before open(): the browser only allows the
// window during the click.
const assets = await Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
select: 'single',
});
if (!assets) {
return; // The person cancelled.
}
const [asset] = assets;
const image = document.getElementById('image');
image.src = asset.url;
image.alt = asset.metadata.alt ?? '';
document.getElementById('credit').textContent = asset.metadata.credit ?? '';
document.getElementById('result').hidden = false;
});
</script>
</body>
</html>
url is the image in the format the person chose in the picker. Store it with your content: it is public, needs no token and keeps working after the integration is deleted. alt and credit are present when the integration exposes them, and null when they are empty.
Install from npm
npm install @freedam/picker
import { open, FreedamPickerError } from '@freedam/picker';
button.addEventListener('click', async () => {
try {
const assets = await open({ workspace: 'acme.freedam.io', integration: 'w5r8n2c4t7bd' });
if (assets) {
insertImage(assets[0]);
}
} catch (error) {
if (error instanceof FreedamPickerError) {
console.warn(error.code, error.message);
}
}
});
The package ships TypeScript types: PickerAsset, PickerImage, PickerVideo, PickerDocument.
Without JavaScript code: the web component
The script also defines a <freedam-picker> element. A click inside it opens the picker with its attributes as options:
<script src="https://acme.freedam.io/sdk/picker/v1/picker.js"></script>
<freedam-picker workspace="acme.freedam.io" integration="w5r8n2c4t7bd" select="multiple" max="5">
<button type="button">Choose from Freedam</button>
</freedam-picker>
<script>
document.querySelector('freedam-picker').addEventListener('freedam-select', (event) => {
console.log(event.detail.assets);
});
</script>
With npm, import '@freedam/picker/element' defines the element.
Several assets
const assets = await Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
select: 'multiple',
max: 10,
});
assets holds the chosen assets in the order the person chose them, 50 at most.
Show the picker in your page
By default the picker opens in a window. To show it over your page instead, pass mode: 'modal':
const assets = await Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
mode: 'modal',
});
To show it inside your page, in a side panel for example, pass mode: 'inline' and the element that receives it:
<div id="freedam-picker" style="height: 640px"></div>
const assets = await Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
mode: 'inline',
container: document.getElementById('freedam-picker'),
});
To open it in a new tab of the browser, at full width, pass mode: 'tab' (version 1.2). The tab closes after the insertion and the browser returns to your page.
open() resolves the same way in every mode. If the browser does not let the picker sign in inside your page, the picker shows an Open Freedam button that continues in a window. If your page sends a Content-Security-Policy, allow the workspace host in frame-src.
Any size of the image
Each asset has a url_template with the text {options} in place of the image options. Replace it to get the size your layout needs, without calling Freedam:
const src = (options) => asset.url_template.replace('{options}', options);
src('w=800'); // 800 pixels wide, original ratio
src('w=800,h=800'); // square, cropped around the focus point
src('fmt=hero'); // a named format of the publication channel
src('w=1600,f=webp'); // WebP, 1600 pixels wide
The server rounds the width up to the next width the publication channel serves; asset.widths lists those widths for a srcset. Width and height are free, as long as the ratio stays between 1:4 and 4:1; cropped images keep the subject in view thanks to the asset's focus point.
Offer only some types
The integration decides which types of asset the picker offers. To narrow them for one button, pass types:
Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
types: ['image'],
});
types accepts 'image', 'video' and 'document'. A type the integration does not offer is ignored. It needs version 1.2.
Edit an earlier choice
Pass the GAIDs you stored to select them again when the picker opens:
const assets = await Freedam.picker.open({
workspace: 'acme.freedam.io',
integration: 'w5r8n2c4t7bd',
select: 'multiple',
preselected: gallery.map((item) => item.gaid),
});
Store usage_id and usage_token with each asset as well (see the next section).
Tell Freedam where the asset is used
Each insertion is recorded on the asset's page in Freedam, so people in the workspace see where the asset is used before they change or delete it. Describe the page with the asset's usage_token, from your editor or your server:
await fetch('https://acme.freedam.io/api/v1/usages', {
method: 'POST',
headers: {
Authorization: `Bearer ${asset.usage_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
page_url: 'https://www.example.com/products/taper-bowl',
reference: 'SKU-1042',
label: 'Taper bowl product page',
}),
});
When the content no longer shows the asset, remove the usage with DELETE /api/v1/usages/{usage_id} and the same token. The token authorizes these two calls for this insertion only. Keep it with your content, out of the published page. Details: Usages.
React
import { open, type PickerAsset } from '@freedam/picker';
import { useState } from 'react';
export function FreedamImageField({ onChange }: { onChange: (asset: PickerAsset) => void }) {
const [error, setError] = useState<string | null>(null);
const choose = () => {
setError(null);
open({ workspace: 'acme.freedam.io', integration: 'w5r8n2c4t7bd' })
.then((assets) => assets && onChange(assets[0]))
.catch((reason) => setError(reason.code));
};
return (
<>
<button type="button" onClick={choose}>Choose from Freedam</button>
{error && <p role="alert">{error}</p>}
</>
);
}
Vue
<script setup lang="ts">
import { open, type PickerAsset } from '@freedam/picker';
const emit = defineEmits<{ change: [asset: PickerAsset] }>();
function choose() {
open({ workspace: 'acme.freedam.io', integration: 'w5r8n2c4t7bd' }).then((assets) => {
if (assets) {
emit('change', assets[0]);
}
});
}
</script>
<template>
<button type="button" @click="choose">Choose from Freedam</button>
</template>
If the window does not open
- Nothing happens,
popup_blocked:open()must run directly in the click handler, before anyawait. - The picker shows that the page is not allowed,
origin_denied: the integration does not list your page's origin. Check the scheme and the port:https://cms.example.comandhttps://cms.example.com:8443are different origins. - The person inserts but nothing arrives: your page sends
Cross-Origin-Opener-Policy: same-origin. Usesame-origin-allow-popupsinstead. - With
mode: 'modal'or'inline', the Open Freedam button appears at once: the browser refuses the picker inside your page. Check that the integration lists your page's origin and that your Content-Security-Policy allows the workspace host inframe-src.