Your Company

For full design control: your own login button

Integrate Upod with a fully custom login button — no <upod-button> custom element required

If you want the Upod client library's session and profile helpers but not the <upod-button> custom element, you can wire up your own button and call client.login() yourself. This gives you full control over markup, styling, copy, and placement.

When to use this

  • Your design system or CSP policy doesn't allow custom elements
  • You're in an SSR framework where custom elements are awkward
  • Your brand team needs pixel-perfect control over the button
  • You want multiple login triggers across the page (banner, nav, settings) sharing one styled component

Step 1: Load the client library

Include the Upod client bundle in your page:

<script src="https://cdn.upod.eu/client/bundle.browser.js"></script>

Step 2: Initialize the client

Initialize Upod with the client_id we issued you:

const client = upod.initialize({
    authority: 'https://account.upod.eu',
    client_id: 'your-client-id',
    redirect_uri: location.origin + location.pathname,
    post_logout_redirect_uri: location.origin + location.pathname,
});

Step 3: Add your own button and wire the click

Use any markup you like. Wire its click handler to client.login():

<button class="my-cookie-button" type="button">Continue with Upod</button>
document.querySelector('.my-cookie-button').addEventListener('click', () => {
    client.login();
});

That's the entire difference from the <upod-button> path — you own the markup and the click handler.

If you want to keep the visual Upod cue without the custom element, our logo is served from account.upod.eu:

  • Dark variant (for light backgrounds): https://account.upod.eu/static/logo.svg?variant=dark
  • Light variant (for dark backgrounds): https://account.upod.eu/static/logo.svg?variant=light
<button class="my-cookie-button" type="button">
    <img
        src="https://account.upod.eu/static/logo.svg?variant=dark"
        alt=""
        aria-hidden="true"
    />
    Continue with Upod
</button>

Accessibility notes

  • Use <button type="button">, not <a> — clicking triggers a JS action, not navigation.
  • Give the button an accessible name via its text content (e.g. "Continue with Upod"). If the button is icon-only, add aria-label.
  • Mark decorative logos with alt="" and aria-hidden="true" so screen readers don't announce them twice.

Step 5: Handle the callback on the redirect URI

After login, Upod redirects the browser back to your redirect_uri with ?code=…&state=… query parameters. Call client.handleCallbackIfNeeded() on every page load at that URI — it's a no-op when there's nothing to handle, and consumes the code when there is:

client.handleCallbackIfNeeded().then(() => {
    client.isAuthenticated().then(loggedIn => {
        if (loggedIn) {
            hideCookieBanner();
            loadPersonalizedContent();
        } else {
            showCookieBanner();
        }
    });
});

Always call handleCallbackIfNeeded() on the page registered as your redirect_uri — without it, the user lands back on your site with login params in the URL but no active session. It's safe to call on every page load.

You can also react to login and logout via callbacks:

client.onLogin(() => {
    hideCookieBanner();
    loadPersonalizedContent();
});

client.onLogout(() => {
    showCookieBanner();
});

Step 6: Use the profile data

Once authenticated, fetch the anonymized profile and use it for personalisation:

function loadPersonalizedContent() {
    client
        .fetch({
            path: '/storage/profile',
            method: 'get',
            query: { purpose: 'personalisation' },
        })
        .then(profile => {
            // profile.gender:    'male' | 'female' | 'other'
            // profile.ageRange:  [number, number | null]
            // profile.residence: string (e.g. 'Amsterdam')
            renderArticles(profile);
        });
}

See Purpose values for the list of allowed purpose strings, and the Anonymized profile data reference for the full schema.

Complete example

<!doctype html>
<html>
    <head>
        <title>My site with a cookie banner</title>
        <script src="https://cdn.upod.eu/client/bundle.browser.js"></script>
        <style>
            .my-cookie-button {
                display: inline-flex;
                align-items: center;
                gap: 0.5rem;
                padding: 0.75rem 1.25rem;
                border: 1px solid currentColor;
                border-radius: 6px;
                background: white;
                cursor: pointer;
                font: inherit;
            }
            .my-cookie-button img {
                height: 1.25em;
                width: auto;
            }
        </style>
    </head>
    <body>
        <div class="cookie-banner" id="cookie-banner">
            <p>How would you like to continue?</p>
            <button id="accept-cookies" type="button">Accept cookies</button>
            <button id="pay-for-privacy" type="button">Pay for privacy</button>
            <button class="my-cookie-button" type="button">
                <img
                    src="https://account.upod.eu/static/logo.svg?variant=dark"
                    alt=""
                    aria-hidden="true"
                />
                Continue with Upod
            </button>
        </div>

        <main id="content"></main>

        <script>
            const client = upod.initialize({
                authority: 'https://account.upod.eu',
                client_id: 'your-client-id',
                redirect_uri: location.origin + location.pathname,
                post_logout_redirect_uri: location.origin + location.pathname,
            });

            document
                .querySelector('.my-cookie-button')
                .addEventListener('click', () => {
                    client.login();
                });

            function hideCookieBanner() {
                document.getElementById('cookie-banner').style.display = 'none';
            }

            function loadPersonalizedContent() {
                client
                    .fetch({
                        path: '/storage/profile',
                        method: 'get',
                        query: { purpose: 'personalisation' },
                    })
                    .then(profile => {
                        document.getElementById('content').textContent =
                            `Personalised for ${profile.gender} in ${profile.residence}`;
                    });
            }

            // Handle the redirect back from Upod
            client.handleCallbackIfNeeded().then(() => {
                client.isAuthenticated().then(loggedIn => {
                    if (loggedIn) {
                        hideCookieBanner();
                        loadPersonalizedContent();
                    }
                });
            });

            client.onLogin(() => {
                hideCookieBanner();
                loadPersonalizedContent();
            });
        </script>
    </body>
</html>

Next steps