Display content file docx (select from browser) on html component

I created a html component:
and a select file input component.

When I select a file from browser, will call query below:

let currentDocument = null;
const container = document.querySelector("#document-container");
currentDocument = fileButton1.files[0]; 
if (!currentDocument) 

docx.renderAsync(currentDocument, container); 

When I run. The console raise error:

I think the reason is because the file has been encoded in bas64.
How can I decoded file?

Hi @Hung_Phan

It looks like you're using the .files property, which doesn't have base64 (the base64 value is in the .value property):

When working with base64 in Retool, you can use atob() to decode it

Are you using an external library? If so, can you share the link?

I'm thinking this script/html may need to be handled within a custom component

Hello, you should use custom component which support js.

Here is my basic demo for your reference.

the code in Iframe Code

  @import url('https://rsms.me/inter/inter.css');
  html { font-family: 'Inter', sans-serif; }
  @supports (font-variation-settings: normal) {
    html { font-family: 'Inter var', sans-serif; }

  * {
    font-family: 'Inter', sans-serif;

  body {
    margin: 0;

  #react {
    height: 100%;
    width: 100%;

  .card {
    min-width: 0;
    min-height: 120px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 12px 24px;
    border-radius: 4px;
    border: 1px solid #cccccc;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    background-color: #ffffff;

  .title-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    line-height: 24px;

  .title {
    font-size: 18px;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;

  .docs-link {
    font-size: 12px;
    font-weight: 500;
    color: #b062bc;
    text-decoration: none;

  .docs-link:hover {
    color: #9846a4;

  .content {
    margin-top: 4px;
    font-size: 12px;
    line-height: 18px;
    font-weight: 400;
    color: #777777;

  .button-container {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;

  .button {
    border: none;
    border-radius: 4px;
    padding: 8px 16px;
    font-weight: 500;
    font-size: 12px;
    cursor: pointer;
    outline: none;
    appearance: none;
    user-select: auto;

  .button--main {
    background-color: #b062bc;
    color: #ffffff;

  .button--main:hover {
    background-color: #9846a4;

  .button--secondary {
    color: #b062bc;
    background-color: white;
    border: 1px solid #b062bc;

  .button--secondary:hover {
    color: #9846a4;
    border: 1px solid #9846a4;

<!-- You can add any HTML/CSS/JS here. UMD versions are required.
Keep in mind that Custom Components are sensitive to bundle sizes, so try using a
custom implementation when possible. -->
<script crossorigin src="https://unpkg.com/core-js-bundle@3.3.2/minified.js"></script>
<script crossorigin src="https://unpkg.com/jszip/dist/jszip.min.js"></script>
<script crossorigin src="https://volodymyrbaydalka.github.io/docxjs/dist/docx-preview.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="react"></div>

<script type="text/babel">
  const MyCustomComponent = ({ triggerQuery, model, modelUpdate }) => (
    <div className="card">
      <div className="title-container">
        <div className="title">Custom component</div>
        <a href="https://docs.retool.com/docs/custom-components" className="docs-link">View docs</a>
      <div className="content">
        {/* The text below is dynamic and specified by the model. */}
      <div className="button-container">
        {/* This button fires a dynamic query (specified in the model) when clicked. */}
          className="button button--main"
          onClick={() => triggerQuery(model.queryToTrigger)}
          Trigger query
        <input type="file" id="files" onChange={()=>aa()}/>
        <div id="document-container" className="overflow-auto flex-grow-1 h-100"></div>
        {/* This button updates the model when clicked. */}
          className="button button--secondary"
          onClick={() => modelUpdate({ displayText: 'The body of this text references "model.displayText", which just changed!' })}
          Update model
  function aa(){ 
        let currentDocument = null;
        const docxOptions = Object.assign(docx.defaultOptions, {
            debug: true,
            experimental: true

        const container = document.querySelector("#document-container");
        const fileInput = document.querySelector("#files");

        currentDocument = fileInput.files[0]; 

        if (!currentDocument) 

        docx.renderAsync(currentDocument, container, null, docxOptions)
            .then((x) => {

  // This is the entrypoint for the React component.
  const ConnectedComponent = Retool.connectReactComponent(MyCustomComponent)
  const container = document.getElementById('react')
  const root = ReactDOM.createRoot(container)
  root.render(<ConnectedComponent />)

Attached pls find app json, you can check it
docx.json (70.6 KB)
by import it to your new create app.

1 Like

I used libs below:

( docx-preview - npm (npmjs.com))

I try use atob() for decode value, it working on file csv. Do not working on docx file.

Yes, the same libs I use.

It work on my side.

Are you on retool cloud? Have got error message in debug?

1 Like