How to Upload and Convert Images To SVG in Nuxt.js

Divine Orji

SVG is the world’s most popular vector image format used by designers, developers, and content creators. It is incredibly lightweight and scalable. It is also editable as code, allowing for customizations that improve SEO, design, and accessibility.

Creating an SVG from scratch can be tricky, but in this tutorial, you will learn how to generate SVG images and code from any image.

You will implement Cloudinary’s vectorize effect to create the SVG image and take advantage of Nuxt’s APIs to upload, get, and convert your SVG images to code.

CodeSandbox & GitHub Repo

View the complete demo for this tutorial on CodeSandbox:

The source code is also available on GitHub.

Prerequisites

To understand the concepts in this tutorial, you should have the following:

  • Basic knowledge of Git, JavaScript, and Vue
  • A GitHub account (create an account here)
  • A Cloudinary account for uploading and converting your images (create an account here)
  • Node.js, Yarn, and Git installed on your PC

Set Up Nuxt.js Project

First, open this starter template URL in your browser to generate a GitHub repo for this project based on a starter template:

Here, you set nuxt-imgtosvg-demo as the name of your repo.

On your PC, open a terminal window and navigate to your preferred folder, then clone your newly created repo with the command below:

1git clone <your-github-repo>

After it has successfully cloned to your PC, navigate into its folder and install its dependencies with yarn:

1cd nuxt-imgtosvg-demo
2yarn

Or using npm:

1cd nuxt-imgtosvg-demo
2npm install

After successfully installing its dependencies, open the project in your preferred code editor and run the code below in your terminal to start a development server:

1yarn dev

Or using npm:

1npm run dev

This command will serve the project with a hot reload on localhost:3000, which you can view in your browser:

Set Up Cloudinary

Get Cloudinary Cloud Name

In a new browser tab open your Cloudinary dashboard and copy your “Cloud Name”:

In the root directory of your project, create a .env file and update it with the code below:

1CLOUDINARY_CLOUDNAME=your-cloudinary-cloud-name

Enable Client-side Uploads

To enable client-side uploads on Cloudinary, you will need an “unsigned upload preset”. In your browser, navigate to Cloudinary’s settings and click on the “Upload” tab, then scroll down to “Upload Presets”:

Click on “Add upload preset”, specify the name of your new preset, and change “Signing Mode” to “Unsigned”, then save the preset:

Take note of your upload preset name.

Install Cloudinary Package for Nuxt.js

In your project’s terminal, run the code below to install the Cloudinary package for Nuxt.js:

1yarn add @nuxtjs/cloudinary

Or using npm:

1npm install @nuxtjs/cloudinary

Configure Cloudinary in Nuxt.js

After its successful installation, open your nuxt.config.js file and update it with the code below:

1export default {
2 // do not remove pre-existing code
3 modules: ['@nuxtjs/axios', '@nuxtjs/cloudinary'],
4
5 cloudinary: {
6 cloudName: process.env.CLOUDINARY_CLOUDNAME,
7 },
8 // do not remove pre-existing code
9};

In the code above, you did the following:

  • Added @nuxtjs/cloudinary to your Nuxt.js modules array
  • Specified your Cloudinary cloudName in a Cloudinary object

Select an Image from your Device

In your code editor, open pages/index.vue and observe the code on lines 16 to 26:

1<input
2 type="file"
3 accept="image/*"
4 name="fileInput"
5 class="hidden"
6 @change="handleChange"
7 ref="fileInput"
8/>
9<!-- ...template code -->
10<button :onClick="handleRef" purple="true">Choose Image</button>

In the code above:

  • The <input /> accepts image files and triggers the handleChange method
  • The class="hidden" hides <input> from the viewer, and ref="fileInput" lets another element reference <input>
  • The <Button> element is a custom component with an onClick prop that triggers the handleRef method

Update the handleRef method Scroll down to your <script> tag and update the handleRef() method with the code below:

1<script>
2 // do not remove pre-existing code
3 export default {
4 methods: {
5 handleRef() {
6 this.$refs.fileInput.click();
7 },
8 },
9 };
10</script>

In the code above, you did the following:

  • Checked for a fileInput ref in your project’s list of $refs
  • Listened for the click() method triggered by fileInput

When a user clicks on the <Button> component containing this handleRef() method, it triggers a click() method on the <input /> and allows the user to select an image from their device.

Update the handleChange method In your <script> tag, update the handleChange() method with the code below:

1<script>
2 // do not remove pre-existing code
3 export default {
4 methods: {
5 handleChange(e) {
6 // console.log(e)
7 const reader = new FileReader();
8
9 reader.readAsDataURL(e.target.files[0]);
10
11 reader.onload = (onloadEvent) => {
12 this.setImage = onloadEvent.target.result;
13 };
14 },
15 },
16 };
17</script>

In this code, you did the following:

  • Created a reader variable that contains an instance of JavaScript’s FileReader() API
  • The reader reads the image file and returns its data as a base64 encoded string, which is passed to setImage

Due to :src="setImage" in line 11 of your pages/index.vue file, <img> will display the selected image on the browser, ready for upload:

Upload your Selected Image to Cloudinary

In the <script> tag of your pages/index.vue, update the handleUpload() method with the code below:

1<script>
2 // do not remove pre-existing code
3 export default {
4 methods: {
5 async handleUpload() {
6 this.setUploadStatus = true;
7
8 if (this.setImage !== '') {
9 // Upload image to Cloudinary
10 const uploadImage = await this.$cloudinary
11 .upload(this.setImage, {
12 upload_preset: 'imgtosvg',
13 })
14 .then((res) => res.public_id);
15 } else {
16 alert('Choose image first!');
17 }
18 },
19 },
20 };
21</script>

In the code above, you did the following:

  • Set the setUploadStatus state to true, which triggers the loading state of the <Button> component on lines 28 to 34. Note that this <Button> contains an onClick prop to trigger the handleUpload() method
  • Checked if the user selected an image
  • Created a uploadImage variable that uploads your selected image to Cloudinary, with upload_preset as its parameter. Replace the value of upload_preset with yours from Cloudinary
  • The upload method returns an Asset object containing the details of the image you just uploaded. You then returned its public_id, setting it as the value for uploadImage

Extract and Display SVG Code

Generate SVG URL with public_id

Update the handleUpload() method with the code below:

1<script>
2 // do not remove pre-existing code
3 export default {
4 methods: {
5 async handleUpload() {
6 this.setUploadStatus = true
7
8 if (this.setImage !== '') {
9 // Upload image to Cloudinary
10 const uploadImage = await this.$cloudinary
11 .upload(this.setImage, {
12 upload_preset: 'your_cloudinary_unsigned_upload_preset_name'
13 })
14 .then((res) => res.public_id)
15
16 // Convert to SVG
17 const url = await this.$cloudinary.image.url(uploadImage, {
18 format: 'svg',
19 effect: 'vectorize',
20 })
21
22 this.setImage = url
23 },
24 }
25 }
26</script>

In the code above, you did the following:

  • Generated an image URL from Cloudinary and set its format to svg and its effect to vectorize
  • Updated the value of setImage to url, which will display your converted image in your browser

Extract SVG code from URL

In your terminal, run the command below to install html-formatter:

1yarn add html-formatter

Or using npm:

1npm install html-formatter

This package lets you display auto-formatted HTML code, making it more readable. In your pages/index.vue, import html-formatter and update the handleUpload() method with the code below:

1<script>
2 import formatter from 'html-formatter';
3
4 export default {
5 methods: {
6 async handleUpload() {
7 this.setUploadStatus = true;
8
9 if (this.setImage !== '') {
10 // Upload image to Cloudinary
11 const uploadImage = await this.$cloudinary
12 .upload(this.setImage, {
13 upload_preset: 'your_cloudinary_unsigned_upload_preset_name',
14 })
15 .then((res) => res.public_id);
16
17 // Convert to SVG
18 const url = await this.$cloudinary.image.url(uploadImage, {
19 format: 'svg',
20 effect: 'vectorize',
21 });
22
23 this.setImage = url;
24
25 // Extract SVG code
26 await this.$axios
27 .$get(url)
28 .then((data) => (this.svgDisplay = formatter.render(data)));
29 } else {
30 alert('Choose image first!');
31 }
32
33 this.setUploadStatus = false;
34 },
35 },
36 };
37</script>

In the code above, you did the following:

  • Imported html-formatter as formatter
  • Fetched the SVG image from its URL with $axios, which returns the SVG code as data
  • Updated the value of svgDisplay to the formatted data, displaying it on the browser
  • Disabled the loading state by changing setUploadStatus to false

Here is the result of your code:

Copy SVG code to Clipboard

In your terminal, run the command below to install copy-to-clipboard:

1yarn add copy-to-clipboard

Or using npm:

1npm install copy-to-clipboard

This package lets you save content to your browser’s clipboard.

In the <script> tag of your pages/index.vue, import copy-to-clipboard and update the copySvg() method with the code below:

1<script>
2 // do not remove pre-existing code
3 import copy from 'copy-to-clipboard';
4
5 export default {
6 methods: {
7 copySvg() {
8 copy(this.svgDisplay);
9 this.clipboardStatus = 'Copied!';
10 setTimeout(() => {
11 this.clipboardStatus = 'Copy to Clipboard';
12 }, 2000);
13 },
14 },
15 };
16</script>

In the code above, you did the following:

  • Imported the copy-to-clipboard package as copy
  • Saved the current value of svgDisplay to your browser’s clipboard with copy()
  • Changed the text of the clipboard button in line 45 to 'Copied!', and back to 'Copy to Clipboard' two seconds later

Conclusion

In this article, you learned how to convert an image to SVG using Cloudinary, extract its SVG code and display it in a Nuxt.js project using the <pre /> tag. Check out these resources below for a deeper understanding of how Nuxt.js or Cloudinary works.

Resources

Divine Orji

Software Engineer and Technical Writer

I am a software engineer passionate about building fast, scalable apps with beautiful user interfaces.