Making an EPUB Manually
It's an ebook file thing
Table of Contents
I was on a cross-country RV trip recently, and to pass the time I was watching YouTube videos, playing The Legend of Zelda: Echoes of Wisdom on my Switch, and reading a lot of online articles. Reading on my phone isn’t ideal, but it is the most convenient. I still have a 4th gen Kindle that my mom got me in 2012, so it got me thinking if I can read web articles on it.
I checked out some web-to-epub converters in extension and website formats, but a lot of them outputted bad typography and poorly sized images. There was one extension that did a decent enough job, but they only gave me 10 free credits to use :/
Dissecting the EPUB
I read somewhere that EPUBs are just a zip file with HTML, CSS, and XML, so if I could hack one together, I could figure out how to make my own converter! Using one of the EPUBs that I converted from an article, I changed the extension to ZIP and started poking around inside.
|-- META-INF/
| |-- container.xml
|-- OEBPS/
| |-- images/
| | |-- image_001.png
| | |-- image_002.png
| |-- text/
| | |-- chapter_001.xhtml
| | |-- chapter_002.xhtml
| | |-- chapter_003.xhtml
| |-- styles/
| | |-- stylesheet.css
| |-- content.opf
| |-- toc.ncx
|-- mimetype
The mimetype
file is the simplest, as it is just this text. It lets ereaders know this is an EPUB file.
application/epub+zip
The next file an ereader checks is META-INF/container.xml
, which points to the root of the EPUB.
<?xml version="1.0" encoding="UTF-8"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>
And that file it’s pointing to is OEBPS/content.opf
, which is just another XML file. This file declares the ebook’s metadata, and a manifest that points to all the files it will be using; chapters, stylesheets, images, and the table of contents (toc.ncx
).
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookID" version="2.0">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
<dc:title>Title of the Book</dc:title>
<dc:creator>Author Name</dc:creator>
<dc:date opf:event="publication">2025-08-21</dc:date>
<dc:language>en</dc:language>
<dc:identifier id="BookID" opf:scheme="CustomID">unique-identifier</dc:identifier>
</metadata>
<manifest>
<item id="toc" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
<item id="styles" href="styles/stylesheet.css" media-type="text/css"/>
<item id="chapter_001" href="text/chapter_001.xhtml" media-type="application/xhtml+xml"/>
...
<item id="image_001" href="images/image_001.png" media-type="image/png"/>
...
</manifest>
<spine toc="toc">
<itemref idref="chapter_001"/>
<itemref idref="chapter_002"/>
<itemref idref="chapter_003"/>
</spine>
</package>
The toc.ncx
is yet another XML file, and it lets you construct nav points that point to not just chapters, but headings within those chapters too.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="en">
<head>
<meta name="dtb:uid" content="unique-identifier"/>
<meta name="dtb:depth" content="1"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle>
<text>Title of the Book</text>
</docTitle>
<docAuthor>
<text>Author Name</text>
</docAuthor>
<navMap>
<navPoint class="chapter" id="navPoint-1" playOrder="1">
<navLabel>
<text>Chapter 1</text>
</navLabel>
<content src="text/chapter_001.xhtml"/>
</navPoint>
...
</navMap>
</ncx>
And finally, the chapter file itself. It’s quite similar to normal HTML pages served on the web, however, it is not HTML. It is XHTML, which requires all elements to be closed (<br />
). Specific to EPUB 2.0, images need to be inside a div
, for some reason. The rest of the files like the CSS are just as you would expect them.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" href="../styles/stylesheet.css" type="text/css" />
<title>Title of the Book</title>
</head>
<body>
<h1>Title of the Book</h1>
<hr />
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<div><img src="../images/image_001.png" /></div>
</body>
</html>
Putting It Back Together
Now that I’ve dissected the EPUB, I turned my first blog post into one and got it working in a browser ereader on my PC. Because Kindles do not support EPUB I needed to convert it to a MOBI, but the two converters I tried were throwing errors. Maybe I made a mistake somewhere?
I found this EPUB validator and ran it on my ebook… Mimetype file entry is missing or is not the first file in the archive.
…okay, that’s weird, cause it is right there. There were other issues that cropped up, including one with only one Google search result from 2006, but I got those fixed.
A quick search later, and the validator expects that mimetype
is not only the first file in the zip archive, but also that it is uncompressed =_= A StackOverflow post suggested renaming it to ##mimetype
so that it is alphabetically sorted to the front, then rename it back to mimetype
from inside the archive. Yeesh.
Anyways, once it passed the validator, it was able to be converted to a MOBI, and after a quick transfer to my Kindle, here is the screenshot! And here is a download to the eblog post :3

Post Mortem
Finding information on the file structure of EPUBs was a little difficult as most people suggest to just use an existing editor that handles it all for you. The validation stuff was annoying, but not too bad. All in all, now that I know how to do this, doing it again will be super easy—and I hope that documenting it here makes it easy for others to try it themselves!
There are two things I want to try next. There is that browser extension idea so I can easily and freely convert articles to EPUBs and read them on my Kindle. The other idea I got after using my blog post as a sample, is to offer EPUBs of my blog posts! I don’t know how hard that would be, but it is something I’d like to learn.

Likes
Annie ????i am honestly amazed that ur kindle still works! i must just have terrible luck with ereaders, every single one ive ever had fails within a year... ;~;