How to print reactjs component in a thermal printer

I’m developing the management part of a delivery system and i am trying to generate a and print in a thermal printer the customers asks. Most of the users will be restaurants, so i wish i could print it without those dialog boxes just when i click in a button to ‘print’ to avoid an exausting work when they have a lot of asks.

I can generate the pdf correctly, but it is not printing right. Thats what i’ve tryied:

function Relatorio(props){

useEffect(() => {
    const element = document.getElementById('wrap-relatorio-pedido'); 
    const opt = {
        margin: 5,
        filename: `pedido #${props.nbl_num_nota}.pdf`,
        image: { type: 'jpeg', quality: 0.98 },
        html2canvas: { scale: 2 },
        jsPDF: { unit: 'mm', format: 'a6', orientation: 'portrait' }
    };

    //first attempt
    // html2pdf().from(element).set(opt).outputPdf('arraybuffer').then((pdf) => {
    //     const blob = new Blob([pdf], { type: 'application/pdf' });
    //     const blobUrl = URL.createObjectURL(blob);

    //     const iframe = document.createElement('iframe');
    //     iframe.style.display = 'none';
    //     iframe.src = blobUrl;
    //     document.body.appendChild(iframe);

    //     iframe.onload = () => {
    //         iframe.contentWindow.print();
    //         document.body.removeChild(iframe);
    //         URL.revokeObjectURL(blobUrl);
    //     };
    // });
    
    
    //second attempt
    // var req = new XMLHttpRequest();
    // req.onload = function (event) {
    //     var blob = new Blob([req.response], {type: 'application/pdf'}); //this make the magic
    //     var blobURL = URL.createObjectURL(blob);
    
    //     const iframe =  document.createElement('iframe'); //load content in an iframe to print later
    //     document.body.appendChild(iframe);
    
    //     iframe.style.display = 'none';
    //     iframe.src = blobURL;
    //     iframe.onload = function() {
    //       setTimeout(function() {
    //         iframe.focus();
    //         iframe.contentWindow.print();
    //       }, 1);
    //     };
    // };

    html2pdf().from(element).set(opt).save();

}, [props.nbl_num_nota]);

const space = ' '; // adiciona espaçamento em spans

const data = new Date(props.nbl_dat_emissao);

// Formatar a data no formato dd/mm/yyyy
const dataFormatada = `${('0' + data.getDate()).slice(-2)}/${('0' + (data.getMonth() + 1)).slice(-2)}/${data.getFullYear()}`;

// Formatar a hora no formato hh:mm
const horaFormatada = `${('0' + data.getHours()).slice(-2)}:${('0' + data.getMinutes()).slice(-2)}`;

return <>
    <div className='d-flex justify-content-between pedido ps-2 pt-3 pb-1' id='body-relatorio-pedido'>
        <div className='row pt-4' id='wrap-relatorio-pedido'>
            //content
        </div>
    </div>
</>
}

export default Relatorio;

I don’t know if it is possible due to the browser security and i have alredy tryied to the other similar question’s solutions… so if anyone know how to do it or can answer if it is possible or not would help a lot.

Hello, this could be a printer driver issue try to see if you have the latest from the manufacturer and not windows generic update the pdf and google possible issues font sizes alone can have this effect. Good luck

note: this code should have parenthese after the return.

return (<>
    <div className='d-flex justify-content-between pedido ps-2 pt-3 pb-1' id='body-relatorio-pedido'>
        <div className='row pt-4' id='wrap-relatorio-pedido'>
            //content
        </div>
    </div>
</>)
}

To sum it up this code has thrown errors on codepen and py tutuor. SyntaxError: Unexpected token < was it your intention to put a div inside a div here?

Thank you, i’ll try to find a new driver for the printer. I was wondering about errors in the code and not thinking about a possible error in the hardware or the OS.

About the elements, they are displaying correctly, i just reduced the amount of tags to fit better in the post. but you can see the full component below:


import './relatorio.css';
import { useEffect } from 'react';
import html2pdf from 'html2pdf.js';
import 'jspdf-autotable';

function Relatorio(props){

    useEffect(() => {
        const element = document.getElementById('wrap-relatorio-pedido'); 
        const opt = {
            margin: 5,
            filename: `pedido #${props.nbl_num_nota}.pdf`,
            image: { type: 'jpeg', quality: 0.98 },
            html2canvas: { scale: 2 },
            jsPDF: { unit: 'mm', format: 'a6', orientation: 'portrait' }
        };
    
        //first attempt
        // html2pdf().from(element).set(opt).outputPdf('arraybuffer').then((pdf) => {
        //     const blob = new Blob([pdf], { type: 'application/pdf' });
        //     const blobUrl = URL.createObjectURL(blob);

        //     const iframe = document.createElement('iframe');
        //     iframe.style.display = 'none';
        //     iframe.src = blobUrl;
        //     document.body.appendChild(iframe);

        //     iframe.onload = () => {
        //         iframe.contentWindow.print();
        //         document.body.removeChild(iframe);
        //         URL.revokeObjectURL(blobUrl);
        //     };
        // });
        

        //second attempt
        // var req = new XMLHttpRequest();
        // req.onload = function (event) {
        //     var blob = new Blob([req.response], {type: 'application/pdf'}); //this make the magic
        //     var blobURL = URL.createObjectURL(blob);
        
        //     const iframe =  document.createElement('iframe'); //load content in an iframe to print later
        //     document.body.appendChild(iframe);
        
        //     iframe.style.display = 'none';
        //     iframe.src = blobURL;
        //     iframe.onload = function() {
        //       setTimeout(function() {
        //         iframe.focus();
        //         iframe.contentWindow.print();
        //       }, 1);
        //     };
        // };

    }, [props.nbl_num_nota]);

    useEffect(() => {
        const element = document.getElementById('wrap-relatorio-pedido');
        window.print(element);
        console.log(element);
    },[props.nbl_num_nota]);

    const space = ' '; // adiciona espaçamento em spans
    
    const data = new Date(props.nbl_dat_emissao);

    // Formatar a data no formato dd/mm/yyyy
    const dataFormatada = `${('0' + data.getDate()).slice(-2)}/${('0' + (data.getMonth() + 1)).slice(-2)}/${data.getFullYear()}`;

    // Formatar a hora no formato hh:mm
    const horaFormatada = `${('0' + data.getHours()).slice(-2)}:${('0' + data.getMinutes()).slice(-2)}`;

    return <>
        <div className='d-flex justify-content-between pedido ps-2 pt-3 pb-1' id='body-relatorio-pedido'>
            <div className='row pt-4' id='wrap-relatorio-pedido'>
                <span className='col-12 pt-4 mt-4 ms-1 ps-3' id='span-num-relat'><b className='me-1'>Pedido #{props.nbl_num_nota}</b>{space} <i className='fa fa-sm fa-calendar-days ms-2'></i> {dataFormatada} {space} <i className='fa fa-sm fa-regular fa-clock ms-2'></i>{horaFormatada}</span>
                {/* <small id='small-endereco-relat' className='col-12 d-block mt-1 mb-3 ms-1 ps-3 text-secondary'>{props.nbl_nome_cliente} - {props.nbl_endereco} - {props.nbl_bairro} - {props.nbl_complemento} - 
                    <b className='text-success'>{space}{new Intl.NumberFormat('pt-BR', {style: 'currency', currency: 'BRL'}).format(props.nbl_total_nota)}</b>
                </small> */}
                <small id='small-endereco-relat' className='col-12 d-block mt-1 mb-2 ms-1 ps-3 text-secondary'><i className='fa fa-sm fa-user'></i>{space}{props.nbl_nome_cliente}</small>
                <small id='small-endereco-relat' className='col-12 d-block mb-2 ms-1 ps-3 text-secondary'><i className='fa fa-sm fa-phone'></i>{space}{props.nbl_tel_cel}</small>
                <small id='small-endereco-relat' className='col-12 d-block mb-2 ms-1 ps-3 text-secondary'><i className='fa fa-sm fa-location-dot'></i>{space}{props.nbl_endereco} - {props.nbl_bairro} - {props.nbl_complemento}</small>
                <small id='small-endereco-relat' className='col-12 d-block mb-2 ms-1 ps-3 text-success'><i className='fa fa-sm fa-dollar-sign'></i>{space}{new Intl.NumberFormat('pt-BR', {style: 'currency', currency: 'BRL'}).format(props.nbl_total_nota)}</small>
                <div className="col-12 row" id='row-pedido-no-card'>
                    {
                        props.itens.map(function(item){
                            return <div className='col-12' key={item.ibl_id}>
                                    <div className='mb-4' id="item-divider"></div>
                                    <div className='row p-3 ps-0'>
                                        <div className='col-1'></div>

                                        <div className='col-10'>
                                            <div className='d-flex justify-content-between align-items-center'>
                                                <small><b>{item.ibl_descricao}</b></small>
                                                <small><b>{new Intl.NumberFormat('pt-BR', {style: 'currency', currency: 'BRL'}).format(item.ibl_valor_total)}</b></small>
                                            </div>

                                            <small className='d-block mb-2'>
                                                {item.ibl_qtde.toLocaleString('pt-BR', {minimumIntegerDigits: 2})} x {new Intl.NumberFormat('pt-BR', {style: 'currency', currency: 'BRL'}).format(item.ibl_valor_total/item.ibl_qtde)}
                                            </small>

                                            {
                                                item.detalhes ?
                                                item.detalhes.map(detalhes => {
                                                    return <div key={detalhes.ibd_id} className='d-block text-secondary'>
                                                        <div className='d-flex justify-content-between div-detalhe'>
                                                            <div>
                                                                <small className='me-3'> - {detalhes.ibd_descricao} </small>
                                                            </div>
                                                            <div>
                                                                <small> {detalhes.ibd_qtde.toLocaleString('pt-BR', {minimumIntegerDigits: 2})}</small>
                                                                <small> x </small>
                                                                <small>{new Intl.NumberFormat('pt-BR', {style: 'currency', currency: 'BRL'}).format(detalhes.ibd_valor_total)} </small>
                                                            </div>
                                                        </div>
                                                    </div>
                                                }) : null
                                            }

                                        </div>

                                    </div>
                            </div>
                        })
                    }
                    
                </div>
            </div>
        </div>
    </>
}

export default Relatorio;

Do i need to use node to use the printer or i really can do it all from the front-end?
Im trying to avoide using the node cause i dont want to especify the printer’s model (something required in most of the libraries that i found).

Hey, you may not have that option but anyway here is a link on the node subject.

node.js - Reactjs web thermal printer - Stack Overflow