Freedam

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 any await.
  • 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.com and https://cms.example.com:8443 are different origins.
  • The person inserts but nothing arrives: your page sends Cross-Origin-Opener-Policy: same-origin. Use same-origin-allow-popups instead.
  • 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 in frame-src.

Try Freedam

Related Documentation

Explore more in this category