How To Update An Existing User in Pocketbase using SvelteKit

No Comments
Published: 25.02.2024

Do you want to learn how to update an existing user in Pocketbase with a new profile picture, a new email, or a new password? In this post, we will create the settings page allowing the user to do exactly that!

Don’t want to read? Watch the video instead!

Create the Update User Form Action

As we already know from the last post, we always need to create a form action if we want to interact with Pocketbase. Therefore we will create a new src/routes/(app)/settings/+page.server.ts file with a default form action. We use the default one, as we will only need one. For now, we will also not enhance this site and create toasts or similar things. You can do these as a practice if you want to!

The form action will first remove all the empty fields, as we will always send all fields but do not require him to fill out all of them. We then get the user ID and update the user with the changed data.

import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';

export const actions: Actions = {
    default: async ({locals, request}) => {
        const data = await request.formData();

        // remove empty fields
        for (const [key, value] of data.entries()) {
            if (value === '') {
                data.delete(key);
            }
        }

        const userId = locals.pb.authStore.model?.id;

        try {
            await locals.pb.collection('users').update(userId,  data)
        } catch(e) {
            console.log(e);
            return fail(400, {unknown: true})
        }

        throw redirect(303, '/dashboard');
    }
};

In case of a successful password change, the user will need to reauthenticate with the password.

Create the Settings Page

The settings page consists of a form with a file button and input fields for email and password. They are all connected to variables, and based on these variables, we determine if we should display a save button. The file button has the name avatar, as this is the property name, we need to update it inside Pocketbase.

<script lang="ts">
    import type { PageData } from './$types';
    import { FileButton } from '@skeletonlabs/skeleton';

    export let data: PageData;

    let avatarFiles: FileList;
    let email: string = data.user.email;
    let oldPassword: string;
    let password: string;

    $: changes = avatarFiles?.length > 0 || email !== data.user.email || oldPassword && password;   
</script>

<form class="h-full w-full" method="POST" enctype="multipart/form-data">
    <div class="h-full flex flex-col justify-center sm:mx-auto sm:w-full sm:max-w-sm gap-2">
        <label class="label" for="avatar">
            <span>Avatar:</span>
            <FileButton name="avatar" bind:files={avatarFiles}>Upload</FileButton>
        </label>

        <label class="label">
            <span>Email:</span>
            <input class="input" type="email" placeholder="mail@example.com" bind:value={email} />
        </label>
        <label class="label">
            <span>Old password:</span>
            <input class="input" name="oldPassword" type="password" placeholder="*****" bind:value={oldPassword} />
        </label>
        <label class="label">
            <span>Password:</span>
            <input class="input" name="password" type="password" placeholder="*****" bind:value={password}/>
            <input type="hidden" name="passwordConfirm" class="input" placeholder="*****" bind:value={password} />
        </label>
    </div>
    {#if changes}
    <footer class="fixed left-0 bottom-0 h-16 w-full bg-surface-900 flex flex-row drop-shadow p-2">
        <button class="ml-auto btn variant-filled-primary" type="submit">Save</button>
    </footer>
    {/if}
</form>

And with that, we already have a working settings page to update an existing Pocketbase user.

Need help or want to share feedback? Join my discord community!

Finally, we will update our header to display the user avatar and use proper Skeleton components. First, we have to get the correct avatar URL. For that, we will go into src/routes/(app)/+layout.server.ts and retrieve the file URL if an avatar is set:

import type { UsersResponse } from '$lib/types/pocketbase';
import { serializeNonPOJOs } from '$lib/utils';
import type { LayoutServerLoad } from './$types';

export const load = (async ({locals}) => {
    const user = locals.pb.authStore.model as UsersResponse;
    if (user.avatar) {        
        user.avatar = await locals.pb.files.getUrl(user, user.avatar, {'thumb': '100x100'});
    }

    return {user: serializeNonPOJOs(user) as UsersResponse};
}) satisfies LayoutServerLoad;

Then we go into src/routes/(app)/+layout.svelte and create an avatar and popup settings variable. Additionally, we will update our profile image using Skeletons Avatar component and then use the Skeleton popup for our profile menu. For the second step, we have to remove some classes from the menu and surround it with another div and a data-popup property.

KOFI Logo

If this guide is helpful to you and you like what I do, please support me with a coffee!

<script lang="ts">
    import { AppBar, AppShell, Avatar, type PopupSettings, popup } from '@skeletonlabs/skeleton';
    import type { LayoutData } from './$types';

    export let data: LayoutData;
    let avatar = data.user.avatar || 'https://ui-avatars.com/api/?name=' + data.user.email;

    const popupClick: PopupSettings = {
        event: 'click',
        target: 'popupClick',
        placement: 'bottom-end',
    };
</script>

<AppShell>
    <svelte:fragment slot="header">
        <AppBar slotTrail="relative">
            <svelte:fragment slot="lead">💵</svelte:fragment>
            <svelte:fragment slot="trail">
                <button use:popup={popupClick}>
                    <span class="sr-only">Your profile</span>
                    <Avatar
                        src="{avatar}"
                        class="!w-10 !h-10"
                        border="border-2 border-surface-300-600-token hover:!border-primary-500"
                        cursor="cursor-pointer"
                    />
                </button>

                <div
                    data-popup="popupClick"
                >
                    <div 
                        class="btn-group-vertical variant-filled"
                        role="menu"
                        aria-orientation="vertical"
                        aria-labelledby="user-menu"
                    >
                        <a
                            href="/settings"
                            role="menuitem"
                            class="w-full"
                        >
                            Settings
                        </a>
                        <form action="/login?/logout" method="post" class="w-full">
                            <button
                                type="submit"
                                role="menuitem"
                                class="w-full"
                            >
                                Log Out
                            </button>
                        </form>
                    </div>
                </div>
            </svelte:fragment>
        </AppBar>
    </svelte:fragment>

    <slot />
</AppShell>

With that we are now also using Skeleton components in our header.

You can also access the current state of the project in this repository and the branch called “course-9”.

Conclusion

In this post, you learned how to update an existing Pocketbase user with things such as a new profile picture or a new email. I hope you liked the course so far. In the next part, we will try to check if the user settings changed and send a success email if they did. See you then.

In case you have any questions, feel free to ask them, and if you want to stay up to date with all my posts, consider subscribing to my monthly newsletter!

[convertkit form=2303042]

Discussion (0)