Hashmark

What is this project?

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.

Technologies & Hosting

Hashmark is built with the following technologies:

  • Frontend: React, TypeScript, Next.js, TailwindCSS
  • Editor: ProseMirror and TipTap
  • Storage: Decentralized hosting on Arweave
  • Hosting: Vercel at hashmark.xyz

Post Editor

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:

  • Markdown Commands: Quickly format text using commands like **bold** or # headings.
  • Slash Command Menu: Add blocks, images, links, and more with the slash command menu.
  • Bubble Menu: Contextual formatting options that appear as you highlight text.
  • Customizable Themes: Easily customize colors and styles using the built-in form.

The editor is built with TipTap (powered by ProseMirror) to deliver a rich editing experience. Output HTML files are fully optimized for SEO, with:

  • Optimized metadata for social sharing and search engines
  • Semantically correct HTML tags
  • Image alt tags
Hashmark EditorThe Hashmark editor interface.Hashmark Editor Slash CommandsThe slash command menu.

Post Viewer

Hashmark makes it easy to load blog posts directly into your own website. Here’s how:

  1. 1. Query the Arweave storage network using the wallet address used to sign and upload the blog post.
  2. 2. Fetch the raw HTML from Arweave.
  3. 3. Sanitize the HTML for security and wrap it in a custom <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 }}
      />
    </>
  );
};
Sample page to fetch and render a blog post. See full implementation with Next.js and SEO optimizations here.
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,
    };
}
Sample function to sanitize and extract components from blog post HTML. See full implementation here.Hashmark OutputExample output HTML page wrapped and styled within a custom Next.js site.Ensemble Blog ScreenshotProduction example of Hashmark blog posts integrated on Ensemble.
View the Code