Hashmark is an open-source blogging platform designed with two unique constraints: it is completely free to use, and modular/customizable for seamless integration into any website. By using decentralized storage on Arweave, Hashmark allows users to host blog posts without paying hosting or storage fees, while developers maintain complete control over styling, themes, and presentation on their own domain.
Hashmark is built with the following technologies:
The Hashmark rich-text editor is inspired by Notion, offering an intuitive writing experience that is highly usable for technical and non-technical users alike. Key features include:
**bold**
or # headings
.The editor is built with TipTap (powered by ProseMirror) to deliver a rich editing experience. Output HTML files are fully optimized for SEO, with:
Hashmark makes it easy to load blog posts directly into your own website. Here’s how:
<style>
tag to match your website’s theme.A complete implementation example using Next.js is available in the GitHub repository. It demonstrates how to use static site optimization for fast loading speeds.
Here is a truncated example:
const Page = async ({ params }: Props) => {
/**
* Fetch the blog post from Arweave. See src/lib/irys/index.ts for implementation details.
*/
const blogPost = await getBlogPost({
authorAddress: params.authorAddress,
slug: params.slug,
});
/**
* Sanitize and process the HTML content for display. See below for implementation details.
*/
const processedResult = await processHtml(blogPost.resourceUrl);
const { theme, style, content } = processedResult;
return (
<>
<style id="hashmark-theme" dangerouslySetInnerHTML={{ __html: theme }} />
<style id="hashmark-style" dangerouslySetInnerHTML={{ __html: style }} />
<div
id="hashmark-body"
className="w-full"
dangerouslySetInnerHTML={{ __html: content }}
/>
</>
);
};
export async function processHtml(url: string) {
// Fetch the HTML
const response = await fetch(url);
const htmlText = await response.text();
// Parse the HTML
const $ = cheerio.load(htmlText);
// Remove script tags
$("script").remove();
// Retrieve specific style tags and the body content
const unsafeTheme = $("#hashmark-theme").html() || "";
const unsafeStyle = $("#hashmark-style").html() || "";
const unsafeContent = $("#hashmark-body").html() || "";
// Sanitize the HTML
const theme = DOMPurify.sanitize(unsafeTheme);
const style = DOMPurify.sanitize(unsafeStyle);
const content = DOMPurify.sanitize(unsafeContent, {
ADD_ATTR: ["target", "rel"],
});
return {
theme,
style,
content,
};
}