├── .gitignore ├── README.md ├── generate-pdf.js ├── img └── heart.png ├── index.html ├── invoice-pdf.css ├── invoice.css ├── invoice.html ├── package.json ├── react-pdf ├── .babelrc ├── README.md ├── components │ ├── Footer.js │ ├── GlobalStyle.js │ ├── Invoice.js │ ├── InvoiceInfo.js │ ├── InvoiceTable.js │ ├── LineItems.js │ ├── Logo.js │ ├── PageCounter.js │ └── Totals.js └── generate-pdf.js ├── web ├── README.md ├── modern-normalize.css ├── scripts.js └── web-base.css └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *.output.pdf 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTML invoice template 2 | 3 | This repo contains an HTML invoice template you can customize to fit your business needs. 4 | 5 | The template is meant to be used either in a browser, or rendered as a PDF with Anvil's [HTML to PDF API](https://www.useanvil.com/docs/api/generate-pdf#html--css-to-pdf). Learn more using Anvil to fill, generate, and sign PDFs on our [developer page](https://www.useanvil.com/developers/). 6 | 7 | Here it is rendered in a browser: 8 | 9 | HTML invoice template 10 | 11 | And rendered as a PDF via the HTML to PDF API: 12 | 13 | HTML to PDF invoice 14 | 15 | ## Rendering as a PDF 16 | 17 | You can render the invoice with plain HTML and CSS or with React and styled-components. For use with React, see the [react-pdf](./react-pdf/README.md) directory. 18 | 19 | First [sign up](https://app.useanvil.com/signup) for Anvil and get your [API key](https://www.useanvil.com/docs/api/getting-started#api-key). 20 | 21 | There is an [example node script](./generate-pdf.js) you can use to generate the PDF from plain HTML and CSS. Run the following command at the root of this repo 22 | 23 | ```sh 24 | $ ANVIL_API_TOKEN= node ./generate-pdf.js && open ./generate-plain-html.output.pdf 25 | ``` 26 | 27 | Vanilla HTML and CSS for the invoice template is in the root of this repo. Feel free to view and edit these files to change the output PDF: 28 | 29 | * [invoice.html](./invoice.html) - the invoice's HTML 30 | * [invoice.css](./invoice.css) - the invoice's CSS 31 | * [invoice-pdf.css](./invoice-pdf.css) - the invoice's PDF-specific CSS 32 | 33 | The script simply reads the HTML and CSS from this repo, then generates a PDF. 34 | 35 | ```js 36 | function buildHTMLToPDFPayload () { 37 | const html = fs.readFileSync('./invoice.html').toString() 38 | const css = 39 | fs.readFileSync('./invoice.css').toString() + 40 | fs.readFileSync('./invoice-pdf.css').toString() 41 | return { 42 | type: 'html', 43 | title: 'HTML Invoice', 44 | data: { 45 | html, 46 | css, 47 | }, 48 | } 49 | } 50 | const exampleData = buildHTMLToPDFPayload() 51 | const { statusCode, data, errors } = await client.generatePDF(exampleData) 52 | ``` 53 | 54 | ## Rendering as a PDF with React 55 | 56 | See the [react-pdf](./react-pdf/README.md) directory for full details on using React to generate an invoice PDF. 57 | 58 | The tl;dr to generate a PDF with React is to `yarn install` at the root of this repo, then run: 59 | 60 | ```sh 61 | ANVIL_API_TOKEN=your_token yarn generate-pdf:react && open ./generate-react.output.pdf 62 | ``` 63 | 64 | ## Running in a browser 65 | 66 | Just get a server running at the root of this repo, and visit `index.html`. e.g. 67 | 68 | ```sh 69 | python -m SimpleHTTPServer 8080 70 | ``` 71 | 72 | Visit http://localhost:8080 73 | 74 | See [index.html](https://github.com/anvilco/html-pdf-invoice-template/blob/main/index.html) for more information 75 | 76 | ## Template provided by Anvil 77 | 78 | This repo is provided by [Anvil](https://www.useanvil.com/developers/). Anvil provides easy APIs for all things paperwork. 79 | 80 | 1. [PDF filling API](https://www.useanvil.com/products/pdf-filling-api/) - fill out a PDF template with a web request and structured JSON data. 81 | 2. [PDF generation API](https://www.useanvil.com/products/pdf-generation-api/) - send markdown or HTML and Anvil will render it to a PDF. 82 | 3. [Etch e-sign with API](https://www.useanvil.com/products/etch/) - customizable, embeddable, e-signature platform with an API to control the signing process end-to-end. 83 | 4. [Anvil Workflows (w/ API)](https://www.useanvil.com/products/workflows/) - Webforms + PDF + e-sign with a powerful no-code builder. Easily collect structured data, generate PDFs, and request signatures. 84 | 85 | Learn more on our [Anvil developer page](https://www.useanvil.com/developers/). See the [API guide](https://www.useanvil.com/docs) and the [GraphQL reference](https://www.useanvil.com/docs/api/graphql/reference/) for full documentation. 86 | 87 | ## License 88 | 89 | MIT 90 | -------------------------------------------------------------------------------- /generate-pdf.js: -------------------------------------------------------------------------------- 1 | // Generate a PDF from the HTML code in this repo. 2 | // 3 | // Usage: 4 | // ANVIL_API_TOKEN= node ./generate-pdf.js && open ./generate-plain-html.output.pdf 5 | 6 | const fs = require('fs') 7 | const path = require('path') 8 | const Anvil = require('@anvilco/anvil').default 9 | 10 | const apiKey = process.env.ANVIL_API_TOKEN 11 | 12 | function buildHTMLToPDFPayload () { 13 | const html = fs.readFileSync('./invoice.html').toString() 14 | const css = 15 | fs.readFileSync('./invoice.css').toString() + 16 | fs.readFileSync('./invoice-pdf.css').toString() 17 | return { 18 | type: 'html', 19 | title: 'HTML Invoice', 20 | data: { 21 | html, 22 | css, 23 | }, 24 | page: { 25 | marginLeft: '60px', 26 | marginRight: '60px', 27 | }, 28 | } 29 | } 30 | 31 | async function main () { 32 | const client = new Anvil({ 33 | apiKey, 34 | }) 35 | 36 | const exampleData = buildHTMLToPDFPayload() 37 | const { statusCode, data, errors } = await client.generatePDF(exampleData) 38 | 39 | if (statusCode === 200) { 40 | const scriptDir = __dirname 41 | const outputFilePath = path.join(scriptDir, 'generate-plain-html.output.pdf') 42 | fs.writeFileSync(outputFilePath, data, { encoding: null }) 43 | } else { 44 | console.log(statusCode, JSON.stringify(errors || data, null, 2)) 45 | } 46 | } 47 | 48 | main() 49 | .then(() => { 50 | process.exit(0) 51 | }) 52 | .catch((err) => { 53 | console.log(err.stack || err.message) 54 | process.exit(1) 55 | }) 56 | -------------------------------------------------------------------------------- /img/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anvilco/html-pdf-invoice-template/bd4513888d47cfa7d6cc272fbb37fe8b79e298b6/img/heart.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Invoice Template 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /invoice-pdf.css: -------------------------------------------------------------------------------- 1 | /* 2 | The styles here for use when generating a PDF invoice with the HTML code. 3 | 4 | * Set up a repeating page counter 5 | * Place the .footer-info in the last page's footer 6 | */ 7 | 8 | .footer { 9 | margin-top: 30px; 10 | } 11 | 12 | .footer-info { 13 | float: none; 14 | position: running(footer); 15 | margin-top: -25px; 16 | } 17 | 18 | .page-container { 19 | display: block; 20 | position: running(pageContainer); 21 | margin-top: -25px; 22 | font-size: 12px; 23 | text-align: right; 24 | color: #999; 25 | } 26 | 27 | .page-container .page::after { 28 | content: counter(page); 29 | } 30 | 31 | .page-container .pages::after { 32 | content: counter(pages); 33 | } 34 | 35 | 36 | @page { 37 | @bottom-right { 38 | content: element(pageContainer); 39 | } 40 | @bottom-left { 41 | content: element(footer); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /invoice.css: -------------------------------------------------------------------------------- 1 | /* 2 | Common invoice styles. These styles will work in a browser or using the HTML 3 | to PDF anvil endpoint. 4 | */ 5 | 6 | body { 7 | font-size: 16px; 8 | } 9 | 10 | table { 11 | width: 100%; 12 | border-collapse: collapse; 13 | } 14 | 15 | table tr td { 16 | padding: 0; 17 | } 18 | 19 | table tr td:last-child { 20 | text-align: right; 21 | } 22 | 23 | .bold { 24 | font-weight: bold; 25 | } 26 | 27 | .right { 28 | text-align: right; 29 | } 30 | 31 | .large { 32 | font-size: 1.75em; 33 | } 34 | 35 | .total { 36 | font-weight: bold; 37 | color: #fb7578; 38 | } 39 | 40 | .logo-container { 41 | margin: 20px 0 70px 0; 42 | } 43 | 44 | .invoice-info-container { 45 | font-size: 0.875em; 46 | } 47 | .invoice-info-container td { 48 | padding: 4px 0; 49 | } 50 | 51 | .client-name { 52 | font-size: 1.5em; 53 | vertical-align: top; 54 | } 55 | 56 | .line-items-container { 57 | margin: 70px 0; 58 | font-size: 0.875em; 59 | } 60 | 61 | .line-items-container th { 62 | text-align: left; 63 | color: #999; 64 | border-bottom: 2px solid #ddd; 65 | padding: 10px 0 15px 0; 66 | font-size: 0.75em; 67 | text-transform: uppercase; 68 | } 69 | 70 | .line-items-container th:last-child { 71 | text-align: right; 72 | } 73 | 74 | .line-items-container td { 75 | padding: 15px 0; 76 | } 77 | 78 | .line-items-container tbody tr:first-child td { 79 | padding-top: 25px; 80 | } 81 | 82 | .line-items-container.has-bottom-border tbody tr:last-child td { 83 | padding-bottom: 25px; 84 | border-bottom: 2px solid #ddd; 85 | } 86 | 87 | .line-items-container.has-bottom-border { 88 | margin-bottom: 0; 89 | } 90 | 91 | .line-items-container th.heading-quantity { 92 | width: 50px; 93 | } 94 | .line-items-container th.heading-price { 95 | text-align: right; 96 | width: 100px; 97 | } 98 | .line-items-container th.heading-subtotal { 99 | width: 100px; 100 | } 101 | 102 | .payment-info { 103 | width: 38%; 104 | font-size: 0.75em; 105 | line-height: 1.5; 106 | } 107 | 108 | .footer { 109 | margin-top: 100px; 110 | } 111 | 112 | .footer-thanks { 113 | font-size: 1.125em; 114 | } 115 | 116 | .footer-thanks img { 117 | display: inline-block; 118 | position: relative; 119 | top: 1px; 120 | width: 16px; 121 | margin-right: 4px; 122 | } 123 | 124 | .footer-info { 125 | float: right; 126 | margin-top: 5px; 127 | font-size: 0.75em; 128 | color: #ccc; 129 | } 130 | 131 | .footer-info span { 132 | padding: 0 5px; 133 | color: black; 134 | } 135 | 136 | .footer-info span:last-child { 137 | padding-right: 0; 138 | } 139 | 140 | .page-container { 141 | display: none; 142 | } 143 | -------------------------------------------------------------------------------- /invoice.html: -------------------------------------------------------------------------------- 1 |
2 | Page 3 | 4 | of 5 | 6 |
7 | 8 |
9 | 13 |
14 | 15 | 16 | 17 | 20 | 23 | 24 | 25 | 28 | 29 | 30 | 33 | 36 | 37 | 38 | 41 | 44 | 45 |
18 | Client Name 19 | 21 | Anvil Co 22 |
26 | 123 Main Street 27 |
31 | Invoice Date: May 24th, 2024 32 | 34 | San Francisco CA, 94103 35 |
39 | Invoice No: 12345 40 | 42 | hello@useanvil.com 43 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
QtyDescriptionPriceSubtotal
2Blue large widgets$15.00$30.00
4Green medium widgets$10.00$40.00
5Red small widgets with logo$7.00$35.00
78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 98 | 99 | 100 | 101 | 102 |
Payment InfoDue ByTotal Due
91 |
92 | Account No: 123567744 93 |
94 |
95 | Routing No: 120000547 96 |
97 |
May 30th, 2024$105.00
103 | 104 | 115 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-pdf-invoice-template", 3 | "version": "1.0.0", 4 | "description": "An HTML invoice template for use in a browser and with HTML to PDF generation.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "generate-pdf:plain-html": "yarn babel-node ./generate-pdf.js", 9 | "generate-pdf:react": "yarn babel-node ./react-pdf/generate-pdf.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/anvilco/html-pdf-invoice-template.git" 14 | }, 15 | "keywords": [ 16 | "pdf", 17 | "html", 18 | "api", 19 | "invoice", 20 | "template" 21 | ], 22 | "author": "Anvil Inc.", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/anvilco/html-pdf-invoice-template/issues" 26 | }, 27 | "homepage": "https://github.com/anvilco/html-pdf-invoice-template#readme", 28 | "dependencies": { 29 | "@anvilco/anvil": "^3.0.0", 30 | "prop-types": "^15.8.1", 31 | "react": "^18.2.0", 32 | "react-dom": "^18.2.0", 33 | "styled-components": "^6.0.7" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.22.11", 37 | "@babel/node": "^7.22.10", 38 | "@babel/preset-env": "^7.22.10", 39 | "@babel/preset-react": "^7.22.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /react-pdf/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react", 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /react-pdf/README.md: -------------------------------------------------------------------------------- 1 | # React to PDF Invoice 2 | 3 | This example uses React and styled-components to generate an invoice PDF via Anvil's [HTML to PDF API](https://www.useanvil.com/docs/api/generate-pdf#html--css-to-pdf). You can use this as a jumping off point to generate a PDF with your own custom React / styled-components code via. 4 | 5 | To run it, `yarn install` at the root of this repo, then run: 6 | 7 | ```sh 8 | ANVIL_API_TOKEN=your_token yarn generate-pdf:react && open ./generate-react.output.pdf 9 | ``` 10 | 11 | See the [generate-pdf.js](./generate-pdf.js) script, and `components` directory here for the code. 12 | 13 | React to HTML to PDF 14 | -------------------------------------------------------------------------------- /react-pdf/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled, { createGlobalStyle } from 'styled-components' 3 | 4 | import GlobalStyle from './GlobalStyle' 5 | 6 | const FooterContainer = styled.div` 7 | margin-top: 30px; 8 | ` 9 | 10 | const Thanks = styled.div` 11 | font-size: 1.125em; 12 | 13 | img { 14 | display: inline-block; 15 | position: relative; 16 | top: 1px; 17 | width: 16px; 18 | margin-right: 4px; 19 | } 20 | ` 21 | 22 | const Info = styled.div` 23 | position: running(footer); 24 | margin-top: -25px; 25 | font-size: 0.75em; 26 | color: #ccc; 27 | 28 | span { 29 | padding: 0 5px; 30 | color: black; 31 | 32 | &:last-child { 33 | padding-right: 0; 34 | } 35 | } 36 | ` 37 | 38 | // The `content` here references `position` from the FooterContainer 39 | const FooterPlacement = createGlobalStyle` 40 | @page { 41 | @bottom-left { 42 | content: element(footer); 43 | } 44 | } 45 | ` 46 | 47 | const Footer = () => ( 48 | 49 | 50 | 51 | hello@useanvil.com|{' '} 52 | 555 444 6666 |{' '} 53 | useanvil.com 54 | 55 | 56 | heart 57 | Thank you! 58 | 59 | 60 | ) 61 | 62 | export default Footer 63 | -------------------------------------------------------------------------------- /react-pdf/components/GlobalStyle.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components' 2 | 3 | const GlobalStyle = createGlobalStyle` 4 | body { 5 | font-size: 16px; 6 | } 7 | 8 | table { 9 | width: 100%; 10 | border-collapse: collapse; 11 | } 12 | 13 | table tr td { 14 | padding: 0; 15 | } 16 | 17 | table tr td:last-child { 18 | text-align: right; 19 | } 20 | ` 21 | 22 | export default GlobalStyle 23 | -------------------------------------------------------------------------------- /react-pdf/components/Invoice.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | import GlobalStyle from './GlobalStyle' 5 | import Logo from './Logo' 6 | import PageCounter from './PageCounter' 7 | import InvoiceInfo from './InvoiceInfo' 8 | import LineItems from './LineItems' 9 | import Totals from './Totals' 10 | import Footer from './Footer' 11 | 12 | const Container = styled.div` 13 | ` 14 | 15 | const lineItems = [ 16 | { 17 | quantity: '2', 18 | description: 'Blue large widgets', 19 | price: '$15.00', 20 | subtotal: '$30.00', 21 | }, 22 | { 23 | quantity: '4', 24 | description: 'Green medium widgets', 25 | price: '$10.00', 26 | subtotal: '$40.00', 27 | }, 28 | { 29 | quantity: '5', 30 | description: 'Red small widgets with logo', 31 | price: '$7.00', 32 | subtotal: '$35.00', 33 | }, 34 | ] 35 | 36 | const Invoice = () => ( 37 | 38 | 39 | 40 | 41 | 50 | 51 | 54 | 55 | 61 | 62 |