Generate PDF & Excel Files Directly in Browser with Quasar — No Backend Needed!
Ivan

Ivan @ivanrochacardoso

About: like programming

Location:
Nazaré Paulista, Brasil
Joined:
Mar 8, 2024

Generate PDF & Excel Files Directly in Browser with Quasar — No Backend Needed!

Publish Date: Jun 27
0 0

By Ivan Rocha Cardoso - Full-Stack Developer at SiGlobal

Have you ever needed to generate a PDF report or Excel spreadsheet directly from your app without depending on a backend? Today I'll show you how to do this using only Vue.js + Quasar.

Works 100% in the browser, exports tables, reports, and dashboards in a simple and professional way.


Introduction: Why generate PDF/Excel on the frontend?

Common use cases:

  • Sales reports that need to be printed or emailed
  • Dashboards with data that users want to save locally
  • Data tables that need to be exported for analysis
  • Offline applications that can't depend on server to generate files
  • Cost reduction of infrastructure (no need for server-side libraries)

Frontend approach advantages:

Performance: Distributed processing on users' devices

Simplicity: No additional APIs to maintain

Offline: Works even without internet connection

Cost: Reduces server load


⚙️ Setup: Starting the project

1. Creating the Quasar project

# Install Quasar CLI (if you don't have it yet)
npm install -g @quasar/cli

# Create new project
quasar create pdf-excel-project

# or
npm init quasar@latest

# Navigate to directory
cd pdf-excel-project
Enter fullscreen mode Exit fullscreen mode

2. Installing required dependencies

# For PDF generation
npm install pdfmake

# For Excel generation
npm install xlsx

# Auxiliary dependencies for types (optional)
npm install --save-dev @types/pdfmake
Enter fullscreen mode Exit fullscreen mode

3. Project structure

src/
├── components/
│   ├── ExportPDF.vue
│   └── ExportExcel.vue
├── pages/
│   └── ExportDemo.vue
└── utils/
    ├── pdfGenerator.js
    └── excelGenerator.js
Enter fullscreen mode Exit fullscreen mode

Practical Example 1: Export QTable to Excel

ExportExcel.vue Component

<template>
  <div class="q-pa-md">
    <q-table
      title="Monthly Sales"
      :rows="salesData"
      :columns="columns"
      row-key="id"
      class="q-mb-md"
    />

    <q-btn 
      color="green" 
      icon="download" 
      label="Export Excel"
      @click="exportToExcel"
    />
  </div>
</template>

<script>
import * as XLSX from 'xlsx'

export default {
  name: 'ExportExcel',
  data() {
    return {
      columns: [
        { name: 'id', label: 'ID', field: 'id', align: 'left' },
        { name: 'product', label: 'Product', field: 'product', align: 'left' },
        { name: 'salesperson', label: 'Salesperson', field: 'salesperson', align: 'left' },
        { name: 'amount', label: 'Amount', field: 'amount', format: val => `$${val}` },
        { name: 'date', label: 'Date', field: 'date' }
      ],
      salesData: [
        { id: 1, product: 'Dell Laptop', salesperson: 'John Smith', amount: 2500.00, date: '2024-01-15' },
        { id: 2, product: 'Logitech Mouse', salesperson: 'Mary Johnson', amount: 85.50, date: '2024-01-16' },
        { id: 3, product: 'Mechanical Keyboard', salesperson: 'Peter Wilson', amount: 350.00, date: '2024-01-17' },
        { id: 4, product: '24" Monitor', salesperson: 'Anna Davis', amount: 800.00, date: '2024-01-18' }
      ]
    }
  },
  methods: {
    exportToExcel() {
      // Prepare data for Excel
      const worksheetData = [
        // Header
        ['ID', 'Product', 'Salesperson', 'Amount', 'Date'],
        // Data
        ...this.salesData.map(row => [
          row.id,
          row.product,
          row.salesperson,
          row.amount,
          row.date
        ])
      ]

      // Create worksheet
      const worksheet = XLSX.utils.aoa_to_sheet(worksheetData)

      // Create workbook
      const workbook = XLSX.utils.book_new()
      XLSX.utils.book_append_sheet(workbook, worksheet, 'Sales')

      // Export file
      XLSX.writeFile(workbook, `sales_${new Date().toISOString().split('T')[0]}.xlsx`)

      this.$q.notify({
        color: 'green-4',
        textColor: 'white',
        icon: 'cloud_download',
        message: 'Excel exported successfully!'
      })
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

📄 Practical Example 2: Generate PDF with Logo and Formatting

ExportPDF.vue Component

<template>
  <div class="q-pa-md">
    <q-card class="q-mb-md">
      <q-card-section>
        <div class="text-h6">Sales Report</div>
        <div class="text-subtitle2">Data visualization</div>
      </q-card-section>

      <q-card-section>
        <q-table
          :rows="salesData"
          :columns="columns"
          row-key="id"
          hide-pagination
          :rows-per-page-options="[0]"
        />
      </q-card-section>
    </q-card>

    <q-btn 
      color="red" 
      icon="picture_as_pdf" 
      label="Generate PDF"
      @click="generatePDF"
    />
  </div>
</template>

<script>
import pdfMake from 'pdfmake/build/pdfmake'
import pdfFonts from 'pdfmake/build/vfs_fonts'

// Configure fonts
pdfMake.vfs = pdfFonts

export default {
  name: 'ExportPDF',
  data() {
    return {
      columns: [
        { name: 'id', label: 'ID', field: 'id', align: 'left' },
        { name: 'product', label: 'Product', field: 'product', align: 'left' },
        { name: 'salesperson', label: 'Salesperson', field: 'salesperson', align: 'left' },
        { name: 'amount', label: 'Amount', field: 'amount', format: val => `$${val}` },
        { name: 'date', label: 'Date', field: 'date' }
      ],
      salesData: [
        { id: 1, product: 'Dell Laptop', salesperson: 'John Smith', amount: 2500.00, date: '2025-01-15' },
        { id: 2, product: 'Logitech Mouse', salesperson: 'Mary Johnson', amount: 85.50, date: '2025-01-16' },
        { id: 3, product: 'Mechanical Keyboard', salesperson: 'Peter Wilson', amount: 350.00, date: '2025-01-17' },
        { id: 4, product: '24" Monitor', salesperson: 'Anna Davis', amount: 800.00, date: '2025-01-18' }
      ]
    }
  },
  methods: {
    generatePDF() {
      // Prepare table data for PDF
      const tableBody = [
        // Table header
        [
          { text: 'ID', style: 'tableHeader' },
          { text: 'Product', style: 'tableHeader' },
          { text: 'Salesperson', style: 'tableHeader' },
          { text: 'Amount', style: 'tableHeader' },
          { text: 'Date', style: 'tableHeader' }
        ],
        // Table data
        ...this.salesData.map(row => [
          row.id.toString(),
          row.product,
          row.salesperson,
          `$${row.amount.toFixed(2)}`,
          row.date
        ])
      ]

      // Calculate total
      const total = this.salesData.reduce((sum, item) => sum + item.amount, 0)

      // Define PDF structure
      const docDefinition = {
        content: [
          // Header
          {
            columns: [
              {
                text: 'SALES REPORT',
                style: 'header'
              },
              {
                text: `Date: ${new Date().toLocaleDateString('en-US')}`,
                style: 'subheader',
                alignment: 'right'
              }
            ]
          },

          // Divider line
          { text: '', margin: [0, 10, 0, 10] },

          // Table
          {
            table: {
              headerRows: 1,
              widths: ['auto', '*', '*', 'auto', 'auto'],
              body: tableBody
            },
            layout: {
              fillColor: function (rowIndex) {
                return (rowIndex % 2 === 0) ? '#f3f3f3' : null
              }
            }
          },

          // Total
          {
            text: `Grand Total: $${total.toFixed(2)}`,
            style: 'total',
            margin: [0, 20, 0, 0]
          },

          // Footer
          {
            text: 'Report automatically generated by the system',
            style: 'footer',
            margin: [0, 30, 0, 0]
          }
        ],

        // Styles
        styles: {
          header: {
            fontSize: 18,
            bold: true,
            color: '#2c3e50'
          },
          subheader: {
            fontSize: 12,
            color: '#7f8c8d'
          },
          tableHeader: {
            bold: true,
            fontSize: 12,
            color: 'white',
            fillColor: '#3498db'
          },
          total: {
            fontSize: 14,
            bold: true,
            alignment: 'right',
            color: '#27ae60'
          },
          footer: {
            fontSize: 10,
            italics: true,
            alignment: 'center',
            color: '#95a5a6'
          }
        }
      }

      // Generate and download PDF
      pdfMake.createPdf(docDefinition).download(`sales_report_${new Date().toISOString().split('T')[0]}.pdf`)

      this.$q.notify({
        color: 'red-4',
        textColor: 'white',
        icon: 'picture_as_pdf',
        message: 'PDF generated successfully!'
      })
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Advanced Improvements

1. Excel with custom formatting

// utils/excelGenerator.js
import * as XLSX from 'xlsx'

export function exportAdvancedExcel(data, filename) {
  const worksheet = XLSX.utils.aoa_to_sheet(data)

  // Apply styles (limited in XLSX)
  const range = XLSX.utils.decode_range(worksheet['!ref'])

  // Cell formatting
  for (let row = range.s.r; row <= range.e.r; row++) {
    for (let col = range.s.c; col <= range.e.c; col++) {
      const cellAddress = XLSX.utils.encode_cell({ r: row, c: col })

      if (row === 0) {
        // Bold header
        if (!worksheet[cellAddress]) continue
        worksheet[cellAddress].s = {
          font: { bold: true },
          fill: { fgColor: { rgb: "3498db" } }
        }
      }
    }
  }

  // Adjust column widths
  worksheet['!cols'] = [
    { width: 10 },  // ID
    { width: 30 },  // Product
    { width: 20 },  // Salesperson
    { width: 15 },  // Amount
    { width: 12 }   // Date
  ]

  const workbook = XLSX.utils.book_new()
  XLSX.utils.book_append_sheet(workbook, worksheet, 'Data')
  XLSX.writeFile(workbook, filename)
}
Enter fullscreen mode Exit fullscreen mode

2. PDF with custom logo

// utils/pdfGenerator.js
import pdfMake from 'pdfmake/build/pdfmake'

export function generatePDFWithLogo(data, logoBase64) {
  const docDefinition = {
    content: [
      // Logo and header
      {
        columns: [
          {
            image: logoBase64, // Base64 image
            width: 100,
            height: 50
          },
          {
            text: 'COMPANY XYZ\nManagement Report',
            style: 'headerWithLogo',
            alignment: 'right'
          }
        ]
      },

      // Rest of content...
    ],

    styles: {
      headerWithLogo: {
        fontSize: 14,
        bold: true,
        color: '#2c3e50'
      }
    }
  }

  return pdfMake.createPdf(docDefinition)
}
Enter fullscreen mode Exit fullscreen mode

Main page integrating everything

indexPage.vue

Replace the contents of the src/pages/indexPage.vue file, use the contents;

<template>
  <q-page class="q-pa-md">
    <div class="text-h4 q-mb-lg text-center">
      📊 Demo: PDF & Excel on Frontend
    </div>

    <div class="row q-gutter-md">
      <div class="col-12 col-md-5">
        <ExportExcel />
      </div>

      <div class="col-12 col-md-5">
        <ExportPDF />
      </div>
    </div>

    <q-separator class="q-my-lg" />

    <div class="text-center">
      <p class="text-body1">
        ✨ No backend, no complications! ✨
      </p>
      <p class="text-caption text-grey-7">
        100% browser file generation using Vue.js + Quasar
      </p>
    </div>
  </q-page>
</template>

<script>
import ExportExcel from 'components/ExportExcel.vue'
import ExportPDF from 'components/ExportPDF.vue'

export default {
  name: 'ExportDemo',
  components: {
    ExportExcel,
    ExportPDF
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Conclusion

With these implementations, you have a complete export system that:

  • Works offline - Doesn't depend on server
  • High performance - Client-side processing
  • Flexible - Easy to customize styles and layouts
  • Professional - Good-looking PDFs and Excel files
  • Scalable - Can be integrated into any Quasar project

Next steps:

  • Implement charts in PDF using Chart.js
  • Add multiple sheets in Excel
  • Create reusable templates
  • Integrate with upload components for logos

🔗 Useful links

- GitHub project files

💡 Tip: Save this tutorial and try out the code! The best way to learn is by practicing.

🚀 Enjoyed the content? Share with other developers and leave your feedback in the comments!

Comments 0 total

    Add comment