I am trying to convert HTML content into a formatted Google Doc. My current workflow uses a Code node to generate the base data, which is then passed to an HTML node to structure the content. I need to take the output of this HTML node and create a new Google Document while preserving the HTML formatting. The standard Google Docs node handles plain text easily, but I am looking for the correct method or workaround to upload the HTML output so that the styling remains intact.
The problem is that the standard Google Docs node in n8n is only designed for plain text. If you try to send HTML code to it, the node doesn’t “read” the styling; it simply prints the raw HTML tags (like <b> or <h3>) directly onto the page, leaving you with an unformatted mess.
To fix this, you have to change your approach: instead of trying to insert text into an existing document, you upload your HTML as a file to Google Drive. By using a specific setting called a “mimeType,” you can tell Google to automatically convert that HTML file into a native Google Doc during the upload process. This allows Google’s own system to handle the formatting, which preserves your headings, bold text, and bullet points.
In practical terms, this requires replacing the Google Docs node with an HTTP Request node. You first use a Code node to bundle your HTML content and document name into a special “multipart” package, and then the HTTP Request node sends that package to the Google Drive API. This workaround bypasses the limitations of the standard node and ensures your final document looks professional and correctly styled.
Below is the JSON for the n8n workflow. You can copy this and paste it directly into your n8n canvas.
Note: You will need to select your own Google Drive OAuth2 credentials in the “Create Google Doc” node after importing, as credentials cannot be exported in JSON.
@Benry_Hendix you’re unfortunately going to find it difficult to reliably convert HTML → Doc. HTML/CSS and Google Docs just work quite differently under the hood. Text, headings and lists should convert pretty easily, but the other components (particularly the more styled ones) will likely look quite different.
Two potential other approaches
- Convert the HTML to markdown and THEN convert that to a Google doc, but that gets pretty messy.
- Convert the HTML to a PDF instead of a Google doc
If anyone has a good solution, I’d be interested to hear, but I don’t know of a reliable method to do HTML → Google Doc conversions.
Not the answer you were hoping for I’m sure, but hopefully somewhat useful.
Alex
The most reliable path I’ve found for this is to skip the Google Docs node and use the Google Docs API’s documents.batchUpdate via HTTP Request node instead. The approach: create a blank doc with the Docs node, then send a batchUpdate request with a series of insertText + updateParagraphStyle requests built from your parsed HTML structure. It’s more work to set up but gives you actual heading styles (HEADING_1, HEADING_2, NORMAL_TEXT) and bold/italic runs that Docs actually recognizes.
If your HTML structure is simple (h1-h3, p, ul/li), a Code node that strips tags and builds the requests array is usually enough - no external libraries needed. Happy to share a code snippet if you can paste a sample of what your HTML output looks like.
The Google Docs node won’t work for this; it only handles plain text. The fix is to upload your HTML directly to Google Drive using its built-in converter. When you upload an HTML file and tell Drive to save it as a Google Doc, it reads the HTML tags and turns them into real formatting, headings, bold text, bullet lists, tables, all of it.
Here is the exact two-step setup:
Step 1 - Add a Code node after your HTML node. This turns your HTML text into a file that can be uploaded
Step 2 - Add an HTTP Request node after that. This is the node that sends your file to Google Drive.
One thing to watch out for: CSS does not carry over. If your styling comes from style= attributes or CSS classes, Google Drive ignores them. It only reads HTML tags like <h1>, <strong>, <ul>, and <table>. If your HTML node is already using those tags, you are good to go.
Here you go
Node: PDF API Hub
Step 1 , pdf to html
step 2, pdf to docx
here is workflow
if you need any changes to the node or format let me know , i can push an update,
You can use the solutions given by the other users first, then try out this, I was experiencing the same problem with Google Docs, and I didn’t want to use Google Drive, so I just created a .gs file in the App Script in Google Docs which triggers every minute (based on my use case) and then it parses the HTML or MD, whatever you are appending to the document after it triggers, you can shorten and lengthen the duration of the trigger based of your needs. Hopefully it fixes the issue for you.
Expanding on the batchUpdate approach above, here’s a Code node snippet that does the conversion of basic HTML (h1–h3, p, strong, ul/li) elements into an array of Docs API requests. This takes place after a blank doc has been created and its ID is available:
</>
// Input: {{ $json.html }} — your HTML string
// Output: array of batchUpdate requests
const html = $input.first().json.html;
const requests = [];
let index = 1;
const lines = html
.replace(/
(.*?)<\/h1>/gi, ‘##H1##$1\n’)
.replace(/
(.*?)<\/h2>/gi, ‘##H2##$1\n’)
.replace(/
(.*?)<\/h3>/gi, ‘##H3##$1\n’)
.replace(/
(.*?)<\/p>/gi, ‘$1\n’)
.replace(/
.replace(/<[^>]+>/g, ‘’)
.split(‘\n’)
.filter(l => l.trim());
for (const line of lines) {
const isH1 = line.startsWith(‘##H1##’);
const isH2 = line.startsWith(‘##H2##’);
const isH3 = line.startsWith(‘##H3##’);
const text = line.replace(/##H[123]##/, ‘’) + ‘\n’;
const style = isH1 ? ‘HEADING_1’ : isH2 ? ‘HEADING_2’ : isH3 ? ‘HEADING_3’ : ‘NORMAL_TEXT’;
requests.push({ insertText: { location: { index }, text } });
if (style !== ‘NORMAL_TEXT’) {
requests.push({
updateParagraphStyle: {
range: { startIndex: index, endIndex: index + text.length },
paragraphStyle: { namedStyleType: style },
fields: 'namedStyleType'
}
});
}
index += text.length;
}
return [{ json: { requests } }];
</>
Pass the requests array to an HTTP Request node hitting POST https://docs.googleapis.com/v1/documents/{docId}:batchUpdate with your Google OAuth2 credentials. CSS won’t carry over, but structural tags will.
