I am building a multi-tenant app. I want the user to upload a profile picture, just for them to see in their interface.
I ‘ve created a bucket profile_pictures
with RLS enabled.
Each user has a folder with their uuid
and can upload images to that folder.
This works. I get back the public url and save that in the table Profiles
under column avatar
.
Ofcourse this doesn’t work, when I add that url to an <Image/>
component it says resource cannot be found. I tried looking into the signedUrl situation but can’t figure it out, also for how long should I sign it then?
I user server side actions and React 19 useFormState
.
This is my upload action:
export const uploadProfilePicture = async (
prevData: any,
formData: FormData
) => {
const user = await requireUser()
const supabase = createSupabaseClient()
const file = formData.get('profile_picture') as File
const fileName = file.name
const { data, error } = await supabase.storage
.from('profile_pictures')
.upload(`${user.id}/${fileName}`, formData.get('profile_picture') as Blob)
if (error) {
return {
status: 'error',
message: error.message,
}
}
const { data: publicURL } = supabase.storage
.from('profile_pictures')
.getPublicUrl(fileName)
const { error: updateError } = await supabase
.from('profiles')
.update({
avatar: publicURL.publicUrl,
})
.eq('id', user.id)
if (updateError) {
console.log(updateError, updateError.message, 'ging niet goed')
return {
status: 'error',
message: 'An error occurred while updating the profile',
}
}
return {
status: 'success',
message: 'Avatar uploaded successfully',
imageUrl: publicURL.publicUrl,
}
}
This is my component:
'use client'
import FormError from '@/components/form-error'
import Image from 'next/image'
import { useEffect, useState } from 'react'
import { useFormState } from 'react-dom'
import toast from 'react-hot-toast'
import { uploadProfilePicture } from '../actions'
const initialState = undefined
const ProfilePicture = () => {
const [formState, formAction] = useFormState(
uploadProfilePicture,
initialState
)
const [imageUrl, setImageUrl] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (formState?.status === 'error') {
setErrorMessage(formState.message)
}
if (formState?.status === 'success') {
setErrorMessage(undefined)
toast.success(formState.message)
}
}, [formState?.status, formState?.message])
useEffect(() => {
if (formState?.status === 'success') {
console.log(formState.imageUrl)
setImageUrl(formState.imageUrl)
}
}, [formState?.status, formState?.imageUrl])
return (
<>
<p>Profilepicture</p>
<form action={formAction} className='my-4'>
<input type='file' name='profile_picture' id='profile_picture' />
<button type='submit'>Upload</button>
<FormError errorMessage={errorMessage} />
</form>
{imageUrl && (
<Image src={imageUrl} alt='Profile Picture' width='200' height='100' />
)}
</>
)
}
export default ProfilePicture
Any help would be greatly appreciated.