i hope you are well. I am working on a medium like text editor and i am using tiptap for that and im implementing it with react. One thing that im stuck in that is uploading images. I want to implement upload image using Image extension or anything else like this:
I want to achive something like this system but i couldn't get close to the solution. I searched about it and i found this gist on github but it didnt help me to create such an extension with upload feature. THE LINK OF THE GITHUB GIST
So i need your help to achive this. thanks for helping.
I finaly found the solution for this.
As i am using tiptap
with react
, and i wanted to let users write caption for images, i decided to use node view
feature of tiptap
extensions. I added addNodeView
to tiptap Image extension
to render my component(you need to extend the Image
extension. you can read about extending a tiptap extension here):
addNodeView() {
return ReactNodeViewRenderer(ImageNode);
}
You can read about creating and rendering node views in tiptap by using vue, react, vanilla js or etc in tiptap docs here
In the next step, i added uploadImageHandler
to existing attributes of Image extension. Like this:
addAttributes() {
return {
...this.parent(),
uploadImageHandler: { default: undefined },
};
}
So when you want to add image with editor.commands.setImage
add your upload image handler function to it. And make sure to not add uploadImageHandler to the HTML attributes of the rendering element. So to do this, you can do something like this(the code might be a bit different for you because i render HTML related to caption):
renderHTML({ HTMLAttributes }) {
return [
'figure',
[
'img',
//This line removed uploadImageHandler from attributes
mergeAttributes(HTMLAttributes, { uploadImageHandler: undefined }),
],
['figcaption', HTMLAttributes.caption],
];
}
There will be a node
prop in the props of the rendering component and you can access to the attributes like this:
const { src, alt, caption, uploadImageHandler } = node.attrs;
Then i added a useEffect
to start uploading after the component is rendered:
useEffect(() => {
//src can be a base64 string. Only upload the image when the src is base64
if (uploadImageHandler && src?.startsWith('data') && !isUploading) {
setIsUploading(true);
uploadImageHandler().then(imgUrl => {
//you can use updateAttributes from the component props
updateAttributes({ src: imgUrl });
setIsUploading(false);
});
}
}, [uploadImageHandler, src, isUploading]);
My uploadImageHandler
looks like this:
const uploadImageHandler = (imgSrc, imgType) => async () => {
const { Image, PreSignedURL } = await getPreSignedUrl(imgType);
await uploadImage({ url: PreSignedURL, image: imgSrc });
return Image;
};
I pass this function to editor.commands.setImage
like this:
editor.commands.setImage({
src: reader.result,
uploadImageHandler: uploadImageHandler(reader.result, imageFile.type),
});
An other solution for this is to execute your uploadImageHandler
in the onload
event of img
element. Base on what you want, you can choose between these two solutions. The onload
solution is not very different with the solution i explained. I couldnt share all parts of my codes but i think you can implement the upload image feature with parts of codes that i shared and things that i explained. I hope it is useful for you.