Embeddable PDFs, the Easy Way

For the last few hours I’ve been tinkering around looking for an efficient and easy way to make viewable / embed PDFs stored in datatables. There are a few fiddling fixes in clone apps on the site but there is a single session answer.

  1. Create a custom HTML form called IFrame and set it as a component with url, string properties and default blank. In the custom HTML, which you can edit in the design view, paste:
<iframe style="width:100%; height: 80vh; border: none" src=""></iframe>

  function setURL(url) {
    $(this).find("iframe").attr("src", url);

Then, in the code view, nick from @meredydd’s IFrame dependency and paste in:

def __init__(self, **properties):
    self.shown = False
    self._url = None

  def url(self):
    return self._url
  def url(self, u):
    self._url = u
    if self.shown:

  def form_show(self, **event_args):
    """This method is called when the HTML panel is shown on the screen"""
    self.shown = True
    if self._url is not None and self._url != "":
      self.call_js('setURL', self._url)
  1. Go to the form where you want to display your PDF and place a custom iFrame component, then in code set your url. For example, in my use case it’s triggered by a ‘View Button’ within a form set up to appear as a fancy alert, so the code is:
def viewbtn_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.docframe.visible = True #Make the frame visible
    docurl = self.item['file'].get_url(False) #Set your url (see the note below too!)
    self.docframe.url = docurl #Connect the url to the IFrame
  1. Bask in the glory of your embedded PDF with fancy pants buttons and the thumbnail page viewer.


  1. The get_url(false) thing is a bit of a cheat as by far the best way of getting a decent url is to create an HTTP endpoint but, if you are using the Users Service and have MFA enabled, making that secure is a tale of woe, coffee, and paracetamol - you have to set up TOTP manually, basically and lose the good, simple, secure by design stuff the good folks of Anvil have taken away from us muppets.

The downside of the cheat is that the link is only good once for your current session, meaning if you view the same file again in the same session, you’ll get a LazyMedia loading error and a black space where your gorgeous PDF once was.

The most efficient way through this isn’t to kill your session though, it’s to make the accompanying interaction with the document meaningful, pressurised, and with an outcome that makes it disappear from view and memory, so you can move users on to the next. In practice, for me this means a task-based activity for the viewer and the added ability to download the media for posterity on a separate button.

I’m sure this is basic, but right now it feels like witchcraft and hasn’t involved a single self.js call for which I am forever grateful.

Hope another Anvil user out there finds this helpful.