Publii: The good, the bad, the uglii

This site is currently being generated using Publii, a relatively simple, open-source, cross-platform, off-line content management system and static site generator all-in -one. If that sounds complicated, you can sleep well tonight knowing that Publii is aimed at us technically challenged users. It's even easier to use than WordPress and since the result is static HTML, there's a lot less worry regarding security. Matter of fact, Publii is currently the only easy to use, off-line, all-in-one CMS/SSG combo i'm aware of that doesn't require a masters degree in web development (slight exaggeration) and this makes it an endangered species that is absolutely necessary in the age of Big Tech and surveillance capitalism.
The fact that there is such a lack of simple website authoring software these days is decidedly disappointing and i think much of the reason for this is a reliance on wildly unethical, privacy-hating, data gulping garbage dumps like FaceSpook, Twatter, TwitTok, Instaspam, etc.. This wasn't always the case as there were several decent desktop WYSIWYG (What-You-See-Is-What-You-Get) website builders available back in the day, some free and open-source, however they have either evaporated or become obsolete. Funny thing is, the trendy "new" thing today is essentially a reinventing of the static site generators of yesteryear and there's no shortage of such projects either, though exceedingly few will resonate with the average person in terms of simplicity.
The majority of today's SSGs are complex, multi-component affairs that cater to the competent web developer with reasonably decent coding skills rather than the blogger who just wants to publish content and not worry so much about all the under-the-hood complexities. Anything remaining that spits out static HTML, and is at least somewhat easy to use, typically requires installation on the server side, such as WordPress. Although running publishing software on the server is the norm, i personally think this is silly and unnecessary in many cases, such as for the solo-web author or where dynamic content is not required.
Publii serves my needs fairly well, but it's not without its issues, some of them rather annoying. That said, Publii has not yet reached the 1.0 mile mark as of this writing and so it's not a finished product. With that in mind, let's beat the hell out of it anyway and see where the pieces land...
the good
- Publii is free, open source software which is reasonably respectful of user privacy.
- It generates static HTML with [too much] CSS and some JavaScript (assuming the default theme), though pretty much everything works for your visitors that don't want to enable JS, including the display of images.
- With no database or CMS running server-side, security concerns are lessened and the server requirements are greatly reduced. Neither PHP or a database is needed.
- While Publii makes it simple to build a website, it's not too dumbed-down. There's still a lot of options for configuring the appearance and functionality of the site, as well as the user interface, though to a far lessor extent in the latter case. Custom themes and post templates are all there if you want them.
- It works off-line which could be the berries for those with crappy internet access. I think this makes perfect sense for the solo or traveling web author verses installing a bloated, server-side CMS and dealing with all the security/privacy issues that entails, it's plug-ins, a database server, PHP, etc..
- Publii offer 2 WYSIWYG editors, at least one of which is based on the open-source TinyMCE editor, as well as a markdown editor, however this is also a sore spot.
- It has a very nice, drag-n-drop, multi-level menu editor.
- It supports tags, including accompanying descriptions and images if desired.
- The UI includes a dark theme, as does the included 'Simple' theme for your website and more boxed themes are available.
- Publii can import content from WordPress, though doing so is ill advised in my opinion since WordPress produces garbage HTML.
- It has a plug-in architecture, though very few plug-ins are currently available and many are not free.
- There are plenty of ways to upload/export your website including FTP, SFTP, a compressed or uncompressed archive and Git integration. There's also Netlify, whatever that is, Google Cloud, Amazon S3 and Cloudflare Pages, all of which everyone should seriously consider avoiding for ethical and privacy reasons.
the bad
- There are very few options for styling elements of the UI which are exposed in the UI, such as font and scrollbar colors, some of which are problematic, at least with the dark theme.
- 3 post editors seems both unnecessary and problematic. I believe a single instance of TinyMCE can reasonably cater to everyone whether they want to write using WYSIWYG (in "block" mode or not), markdown, or edit raw HTML and though i haven't looked under the hood, i suspect Publii may be using TinyMCE for all 3 editing modes which, if true, makes separating them all the more more nonsensical in my opinion. Sure, the WYSIWYG editor can create a mess if one indiscriminately copies and pastes content without removing the formatting (try Ctrl+Shift+V) so some care has to be taken, or filtering with TinyMCE which i'm sure is possible, but not implemented. The other problem with having multiple editors is that a post authored in one cannot be edited in another and this is a complaint of many users.
- TinyMCE options are not easily exposed. For example the status bar is not enabled, a widget which can be very helpful toward avoiding messy or unintentionally nested HTML, and also when selecting content (insert a
<pre>element or an image gallery and then try deleting it with only the mouse). - There's no option to 'sticky' a post so that it remains at the top of the blog page and, again, the developers have no intention of adding this functionality.
- Having a static website, while a plus for some, can be a show-stopper for others. For example it's not entirely trivial (as i'm finding out) to implement a proper, privacy-respecting, self-hosted comment server and running a commercial or very large site would likely be problematic at best.
- It's not possible to bulk add, edit, or remove tags for posts or perform some other useful bulk operations and, once again, the developers are resistant to adding such functionality.
- There's several many annoyances with the WYSIWYG editor, such as a useless and incredibly annoying text formatting pop-up that gets in your way when a word is highlighted, and exactly which formatting icons will be displayed in the pop-up seems to be a roll of the dice. Also there's no tool-tip showing the target of a hovered hyperlink and editing the text of a hyperlink is difficult without opening the link properties dialog, yet another problem the developers have no intention of fixing. In brief, the changes they have made to TinyMCE have compromised some of it's functionality and have made working with it more difficult than need be.
- For larger sites with a lot of content, it may require significant time to sync the local version with the remote server. For example, Publii doesn't make use of Server Side Include (SSI), so if one has 1000 posts and makes a change to the navigation menu, then all 1000 have to be re-uploaded.
- Using the image insert function of the WYSIWYG editor as an example, you can add an image from the dedicated media folder and the link in the HTML editor will start with
file:///which Publii will then convert to match your domain upon syncing, however Publii fails to convert the URL when adding the exact same code when the source is any other folder. - Like WordPress, the way the media library is handled is a mess. One cannot organize content in sub-folders for example and any post which references a file in the media library results in the creation of another copy of the file. Add responsive images to that and it just gets worse. The developers seemed to have learned nothing from the super annoying shortcomings of the WordPress media library.
- On the local side, Publii stores most of your content in an sqlite database though i'd personally prefer flat-files.
- While there is a site-wide RSS/JSON feed, there are no tag feeds, so one cannot subscribe to a preferred topic without subscribing to all of them.
the uglii
Publii leverages a massive pile of 3rd party software including the bloated Electron framework for its UI.
click here to reveal the list as of Publii v0.44
abbrev abort-controller adm-zip agent-base agent-base ajv ansi-colors ansi-diff-stream ansi-regex ansi-regex ansi-regex ansi-styles any-base archiver-utils archiver array-find-index arrify asn1 assert-plus async-lock async-retry async asynckit aws-crypto/crc32 aws-crypto/crc32c aws-crypto/ie11-detection aws-crypto/sha1-browser aws-crypto/sha256-browser aws-crypto/sha256-js aws-crypto/supports-web-crypto aws-crypto/util aws-sdk/client-s3 aws-sdk/client-sso aws-sdk/client-sts aws-sdk/core aws-sdk/credential-provider-env aws-sdk/credential-provider-ini aws-sdk/credential-provider-node aws-sdk/credential-provider-process aws-sdk/credential-provider-sso aws-sdk/credential-provider-web-identity aws-sdk/middleware-bucket-endpoint aws-sdk/middleware-expect-continue aws-sdk/middleware-flexible-checksums aws-sdk/middleware-host-header aws-sdk/middleware-location-constraint aws-sdk/middleware-logger aws-sdk/middleware-recursion-detection aws-sdk/middleware-sdk-s3 aws-sdk/middleware-sdk-sts aws-sdk/middleware-signing aws-sdk/middleware-ssec aws-sdk/middleware-user-agent aws-sdk/region-config-resolver aws-sdk/signature-v4-multi-region aws-sdk/token-providers aws-sdk/types aws-sdk/util-arn-parser aws-sdk/util-endpoints aws-sdk/util-locate-window aws-sdk/util-user-agent-browser aws-sdk/util-user-agent-node aws-sdk/util-utf8-browser aws-sdk/xml-builder aws-sign2 aws4 b4a balanced-match base64-js bcrypt-pbkdf better-sqlite3 bignumber.js bindings bl bmp-js bowser brace-expansion brace-expansion buffer-crc32 buffer-equal-constant-time buffer-equal buffer-from buffer buildcheck cacheable-lookup cacheable-request call-bind camel-case camelcase-keys camelcase caseless chownr clean-css clean-css clean-git-ref cliui clone-response codemirror codemirror-advanceddialog codemirror-revisedsearch codemirror-spell-checker codemirror color-convert color-name color-string color colors combined-stream commander component-props component-xor compress-commons compressible concat-map concat-stream config-chain core-util-is count-files cpu-features crc-32 crc32-stream currently-unhandled dashdash debug debug decamelize decode-uri-component decompress-response deep-extend defer-to-connect delay delayed-stream delete-empty detect-libc diff3 dom-iterator dom-walk dompurify duplexify easymde ecc-jsbn ecdsa-sig-formatter editorconfig electron electron-devtools-installer emoji-regex encoding end-of-stream ent err-code error-ex escalade escape-html event-target-shim exif-parser exit-on-epipe expand-template extend-shallow extend extsprintf fast-deep-equal fast-fifo fast-json-stable-stringify fast-memoize fast-text-encoding fast-xml-parser feathericons file-type file-uri-to-path filter-obj find-up follow-redirects follow-redirects forever-agent form-data form-data fs-constants fs-extra fs.realpath ftp function-bind gaxios gcp-metadata get-caller-file get-intrinsic get-stdin get-stream getpass gifwrap gitbeaker/core gitbeaker/node gitbeaker/requester-utils github-from-package github glob glob global google-auth-library google-cloud/paginator google-cloud/projectify google-cloud/promisify google-cloud/storage google-p12-pem got graceful-fs gtoken handlebars handlebars har-schema har-validator has-symbols has he hosted-git-info html-minifier http-cache-semantics http-proxy-agent http-signature http2-wrapper https-proxy-agent https-proxy-agent iconv-lite ieee754 ieee754 ignore image-downloader image-q image-size immediate indent-string inflight inherits ini is-arrayish is-arrayish is-core-module is-extendable is-finite is-fullwidth-code-point is-fullwidth-code-point is-function is-stream is-typedarray is-utf8 isarray isarray isbinaryfile isomorphic-fetch isomorphic-git isstream jimp/bmp jimp/core jimp/custom jimp/gif jimp/jpeg jimp/plugin-blit jimp/plugin-blur jimp/plugin-circle jimp/plugin-color jimp/plugin-contain jimp/plugin-cover jimp/plugin-crop jimp/plugin-displace jimp/plugin-dither jimp/plugin-fisheye jimp/plugin-flip jimp/plugin-gaussian jimp/plugin-invert jimp/plugin-mask jimp/plugin-normalize jimp/plugin-print jimp/plugin-resize jimp/plugin-rotate jimp/plugin-scale jimp/plugin-shadow jimp/plugin-threshold jimp/plugins jimp/png jimp/tiff jimp/types jimp/utils jimp jpeg-js jquery jquery-ui js-beautify jsbn json-bigint json-buffer json-schema-traverse json-schema json-stringify-safe jsonfile jsprim jszip jwa jws keytar keyv lazystream li libvips lie load-bmfont load-json-file locutus lodash.defaults lodash.difference lodash.flatten lodash.flattendeep lodash.isplainobject lodash.throttle lodash.union loud-rejection lower-case lowercase-keys lru-cache lru-cache ls-all lucide map-obj marked marked material-colors meow mime-db mime-types mime mime mimic-response mimic-response min-document minimatch minimatch minimist minimisted mkdirp-classic mkdirp mkdirp moment ms ms nan nanobus nanotiming napi-build-utils neat-log neo-async nested-sortable netrc no-case node-abi node-addon-api node-addon-api node-fetch node-forge node-slug node-version-compare nopt normalize-package-data normalize-path normalize-url normalize.css oauth-sign object-assign object-inspect omggif once p-cancelable p-limit pako param-case parse-bmfont-ascii parse-bmfont-binary parse-bmfont-xml parse-headers parse-json path-exists path-is-absolute path-parse path-starts-with path-type peek-readable performance-now phin pify pify pinkie-promise pinkie pixelmatch pngjs pngjs prebuild-install prettier-bytes printj prismjs process-nextick-args process promise-retry proto-list pseudomap psl pump punycode punycode qs qs query-string queue-tick queue quick-lru rc read-pkg-up read-pkg readable-stream readable-stream readable-stream readable-web-to-node-stream readdir-glob redent regenerator-runtime relateurl repeating request require-directory resolve-alpn resolve responselike retry-request retry retry rimraf rimraf safe-buffer safe-buffer safer-buffer sax select2 semver semver semver semver setimmediate sha.js sharp side-channel sigmund signal-exit simple-concat simple-get simple-swizzle sindresorhus/is slug smithy/abort-controller smithy/chunked-blob-reader-native smithy/chunked-blob-reader smithy/config-resolver smithy/credential-provider-imds smithy/eventstream-codec smithy/eventstream-serde-browser smithy/eventstream-serde-config-resolver smithy/eventstream-serde-node smithy/eventstream-serde-universal smithy/fetch-http-handler smithy/hash-blob-browser smithy/hash-node smithy/hash-stream-node smithy/invalid-dependency smithy/is-array-buffer smithy/md5-js smithy/middleware-content-length smithy/middleware-endpoint smithy/middleware-retry smithy/middleware-serde smithy/middleware-stack smithy/node-config-provider smithy/node-http-handler smithy/property-provider smithy/protocol-http smithy/querystring-builder smithy/querystring-parser smithy/service-error-classification smithy/shared-ini-file-loader smithy/signature-v4 smithy/smithy-client smithy/types smithy/url-parser smithy/util-base64 smithy/util-body-length-browser smithy/util-body-length-node smithy/util-buffer-from smithy/util-config-provider smithy/util-defaults-mode-browser smithy/util-defaults-mode-node smithy/util-endpoints smithy/util-hex-encoding smithy/util-middleware smithy/util-retry smithy/util-stream smithy/util-uri-escape smithy/util-utf8 smithy/util-waiter sortablejs source-map spdx-correct spdx-exceptions spdx-expression-parse spdx-license-ids split-on-first sqlstring ssh2-sftp-client ssh2 sshpk status-logger stream-consume stream-events stream-shift streamx strict-uri-encode string-width string-width string_decoder string_decoder string_decoder strip-ansi strip-ansi strip-bom strip-indent strip-json-comments striptags strnum strtok3 stubs szmarczak/http-timer tabler-icons tar-fs tar-fs tar-stream tar-stream teeny-request through2 timm tinycolor2 tinycolorpicker tinymce token-types tokenizer/token tootallnate/once tough-cookie tr46 transliteration tree-flatten trim-newlines tslib tslib tunnel-agent tweetnacl typedarray types/cacheable-request types/codemirror types/estree types/http-cache-semantics types/keyv types/marked types/node types/node types/responselike types/tern typo-js uglify-js unescape unicode universalify unzip-crx-3 upper-case uri-js utif2 util-deprecate uuid uuid uuid validate-npm-package-license verror vue-color vue-i18n vue-multiselect vue-prism-editor vue-router vue vuedraggable vuex webidl-conversions whatwg-fetch whatwg-url wordwrap wrap-ansi wrap-ansi wrappy xcase xhr xml-parse-from-string xml2js xmlbuilder xregexp xtend y18n yaku yallist yallist yargs-parser yargs yocto-queue zip-stream
These dependencies, which the developers have little or no control over, have already and will continue to cause problems (see here, here, here, here, here, here, here and here for example). I don't know what the long term plans are, but if the developers intend on sticking with this approach, they're making a big mistake in my opinion. These "apps" that ride on Electron and other such frameworks, often lack a native look and feel and fail to provide the expected functionality on any platform. For example, Publii ignores any native spelling dictionaries, nor can you add words to the built-in one. The developers would do well to read Using a framework can make you stupid!, To framework or not to framework?, So-called modern web developers are the culprits, and How to write software that will keep working for decades without problems.
the verdict
I have tested a sizable bunch of CMS/SSG software which i thought might meet my needs and which included, or could potentially accommodate a proper visual editor, among them automad, bludit, cmsimple, craftercms, datenstrom, dokuwiki, flatpress, flextype, foswiki, guppy, htmly, lnblog, monocms, mozilo, pluxml, typesetter and wondercms. In the end none of them really grabbed me and though Publii currently has many issues, i found it to be better suited to my needs overall given my preferences and fun-flow ('cause writing usually isn't work), plus the off-line, desktop installable aspect was a big sell.
One of my worries is that the developers seem unusually resistant to addressing some of the bugs, annoyances and reasonable feature requests. For example there's no reason whatsoever to have the post title consume as much screen real estate as it does in the editor, yet the suggestion to size it down, or move it to the sidebar, was flatly denied.
Even though Publii is far from being considered stable, their interaction with the community is somewhat sporadic which seems odd for such a young and ambitious project with no shortage of issues, more than 250 as of this writing and they seem to keep piling up. For example there are quite a few issues both on the forum and the code repository which were never answered, passed off as trivial, or given the "wontfix" label. The developers seem to be more concerned with adding features than fixing the most basic problems, like not being able to configure Piblii's data directory, etc..
Overall Publii is very usable as it is but a lot more work is certainly needed and, if the extreme dependency on 3rd party software continues, it will never be a proper product in my opinion. If you're wanting to build a simple, static website without all the worries and headaches of databases and plug-ins and whatnot, i wouldn't let the negatives stop you from giving it a spin, and surely some of the current bugs will be addressed at some point. I would also say that it's unlikely you'll experience all of the problems i have since i used it to convert this website from a ClassicPress affair (WordPress without the "block" editor) using an HTML backup rather than an XML export and this required a significant amount of work and a rather long shell script.
All that said, i'm not sure Publii will survive. While the concept of the project and effort of the developers is greatly appreciated, the way they're handling it flawed in my opinion. I was even chastised by Bob Mitro, one of the two developers, who essentially suggested i fuck off and fork Publii after i had submitted more bug reports than he cared to deal with. Although it can be disappointing to learn of bugs in the code i write, i have always gone to any length necessary to fix them rather than act like a dickhead, and i gained a degree of satisfaction in the process knowing that my code was a bit more robust.
Setting aside the possibility that Publii may go belii-up at some point, i think it's a project that's well-worth checking out, though primarily because it's pretty much the only project of its type at the moment. Hopefully someone will take Bob's advice, fork Publii, and be more appreciative of those providing feedback. Relying on piles of 3rd party code that they can't fix is a mistake in my opinion.