Appearance
Pagedjs is an open-source library to display web pages into beautiful PDFs.
First, let's make a new Vue project
npm create vue@latest
Then follow the instructions from your terminal.
The pagedjs library is available through a script, a command line or a npm module. We'll focus on the npm module here but feel free to go check their documentation for more solutions.
Install the pagedjs npm module
npm install --save pagedjs
Now you are ready to add style for pagedjs to create your beautiful PDF.
Let's see how we could create a nice PDF from this Vue component
<template>
<main>
<section class="chapter">
<h2>About</h2>
<p>Lorem ipsum dolor sit amet consectetur adipiscing elit. Duis nibh tortor</p>
</section>
<section class="chapter">
<h2>Chapter 1</h2>
<p>Lorem ipsum dolor sit amet</p>
</section>
<section class="chapter">
<h2>Chapter 2</h2>
<p>consectetur adipiscing elit</p>
</section>
<section class="chapter">
<h2>Chapter 3</h2>
<p>Duis nibh tortor, pellentesque eu suscipit vel</p>
</section>
</main>
</template>
You can leave the default style generated during the creation of the project (in the assets
folder, files base.css
and app.css
).
Let's start by defining the size of our pages and the margins. These rules can be added directly in the style of the component
<style>
@page {
size: A4;
margin: 20mm 15mm 26mm 15mm;
}
</style>
Let's make every start of a new chapter a new page.
h2 {
break-before: page;
}
Execute pagedjs script when the page loads adding this code to the setup script of your component.
<script setup>
import { onMounted } from 'vue';
import { Previewer } from 'pagedjs';
onMounted(() => {
let paged = new Previewer();
paged.preview().then((flow) => {
console.log('Rendered', flow.total, 'pages.');
});
});
</script>
Now you'll see, the preview of your PDF directly into the browser.
INFO
The previewer modifies your DOM once when preview()
is called, and will not update it if some content changes afterward.
If not all your content is available on load, you can wait for it instead before calling the previewer.
What if you want to display the name of the chapter you are in at the bottom of the PDF ?
Let's add the following css to your component style.
@page {
@bottom-center { content: string(title); }
}
.chapter > h2 {
string-set: title content(text);
}
The first block, is used to tell pagedjs to add content in the margin of the PDF, at the bottom center position (check all the positions available).
The second block, is used to make available the title of the chapter to pagedjs, we use the named strings feature from pagedjs to give the content of the h2 tag from every chapter into the named string "title".
It is also possible to display content in the PDF declaring custom attributes into the HTML.
First, to every section
tag in your component, add a custom attribute data-*
where you replace *
with whatever name you want to give to your content.
For example adding a data-reference
to every chapter.
<section class="chapter" data-reference="001">
<h2>Chapter 1</h2>
<p>Lorem ipsum dolor sit amet</p>
</section>
<section class="chapter" data-reference="002">
<h2>Chapter 2</h2>
<p>consectetur adipiscing elit</p>
</section>
<section class="chapter" data-reference="003">
<h2>Chapter 3</h2>
<p>Duis nibh tortor, pellentesque eu suscipit vel</p>
</section>
Then, add the following to your component style.
@page {
@right-middle { content: string(ref); }
}
.chapter {
string-set: ref attr(data-reference);
}
The first block, is used to tell pagedjs where to display the content (just as before, you can check all the positions available).
The second block, is used to make available the reference to pagedjs, we use the generated text feature from pagedjs.
One of the exciting new feature of Vue3 is the use of v-bind
in CSS, allowing data from the component to be used directly inside CSS instead of going through inline styling.
This will work just fine for styling the content of the pages, but not for styling the content of the margins.
As Vue explains in their documentation:
The actual value will be compiled into a hashed CSS custom property, so the CSS is still static. The custom property will be applied to the component's root element via inline styles and reactively updated if the source value changes.
And, as you will see if you look into the Devtool Elements panel, pagedjs creates a whole bunch of HTML elements to render the PDF correctly, copying your application inside it.
This means the CSS variables defined inline by Vue will not be available for contents in the margin of the PDF. It will not work either if you go through string-set
first. The data will be undefined.
However, the custom attributes are copied into the HTML elements. So if you need data to style the margins, please use that instead.
Be sure to check the pagedjs documentation to see all the features available and how you can use them to render beautiful PDFs !
Paged.js - Installation with React