<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Fayeed Pawaskar]]></title><description><![CDATA[Full-Stack Engineer building AI products, open source libraries, and high-impact web experiences.]]></description><link>https://fayeed.dev</link><image><url>https://fayeed.dev/icon.png</url><title>Fayeed Pawaskar</title><link>https://fayeed.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 25 Feb 2026 07:43:19 GMT</lastBuildDate><atom:link href="https://fayeed.dev/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Tue, 28 Jul 2020 04:00:00 GMT</pubDate><copyright><![CDATA[2026 Fayeed Pawaskar]]></copyright><language><![CDATA[en]]></language><managingEditor><![CDATA[fayeed52@gmail.com]]></managingEditor><webMaster><![CDATA[fayeed52@gmail.com]]></webMaster><ttl>60</ttl><item><title><![CDATA[Building next-agent-md — Markdown for AI Agents in Next.js]]></title><description><![CDATA[<p>A few weeks ago, Cloudflare published a feature called <a href="https://blog.cloudflare.com/markdown-for-agents/">Markdown for Agents</a>.</p>
<p>The idea is simple: if an AI agent requests a page with <code>Accept: text/markdown</code>, return Markdown instead of HTML.</p>
<p>That matters because token cost drops a lot. In Cloudflare's example, the same page went from <strong>16,180 tokens (HTML)</strong> to <strong>3,150 tokens (Markdown)</strong>, roughly an <strong>80% reduction</strong>.</p>
<p>The catch: their solution is Cloudflare-specific. I wanted the same behavior for any Next.js app, regardless of infra, so I built <strong>next-agent-md</strong>.</p>
<p>This post is the architecture story behind it: what I built, why I chose this shape, and which tradeoffs I accepted.</p>
<p>GitHub: <a href="https://github.com/fayeed/next-agent-md">fayeed/next-agent-md</a></p>
<hr />
<h2 id="howitworks">How it works</h2>
<p>The core mechanism is HTTP content negotiation via the <code>Accept</code> header.</p>
<p>If a request includes <code>Accept: text/markdown</code>, middleware intercepts it and returns Markdown.</p>
<p>The first design choice was <strong>where</strong> to put this behavior. I chose middleware instead of route handlers because I wanted one integration point for the entire app. In practice, that keeps adoption dead simple: install package, add middleware once, and every page can serve markdown to agents.</p>
<h3 id="requestflow">Request flow</h3>
<ol>
<li>Agent sends <code>GET /about</code> with <code>Accept: text/markdown</code></li>
<li>Middleware detects markdown intent</li>
<li>Middleware self-fetches the same URL with <code>Accept: text/html</code></li>
<li>HTML boilerplate (nav/footer/scripts/etc.) is stripped</li>
<li>Clean HTML is converted to Markdown</li>
<li>Response returns as <code>text/markdown</code> with token metadata</li>
</ol>
<p>I intentionally convert from already-rendered HTML instead of trying to reconstruct content from React internals. That decision keeps the package router-agnostic (App Router and Pages Router) and makes it resilient to how the app is authored.
It also keeps the architecture honest: Next.js already knows how to render your page correctly, so <code>next-agent-md</code> should transform that output, not reimplement rendering logic.</p>
<h3 id="coremiddlewaresimplified">Core middleware (simplified)</h3>
<pre><code class="ts language-ts">import { NextRequest, NextResponse } from "next/server"
import { NodeHtmlMarkdown } from "node-html-markdown"

const SKIP_HEADER = "x-markdown-skip"

function wantsMarkdown(req: NextRequest) {
  return req.headers.get("accept")?.includes("text/markdown")
}

async function fetchHtml(req: NextRequest) {
  const headers = new Headers(req.headers)
  headers.set("accept", "text/html")
  headers.set(SKIP_HEADER, "1")

  const res = await fetch(req.url, {
    method: "GET",
    headers,
  })

  return res.text()
}

export async function middleware(req: NextRequest) {
  if (req.headers.get(SKIP_HEADER) === "1") return NextResponse.next()
  if (!wantsMarkdown(req)) return NextResponse.next()

  const html = await fetchHtml(req)
  const stripped = stripBoilerplate(html)
  const markdown = NodeHtmlMarkdown.translate(stripped)

  return new NextResponse(markdown, {
    headers: {
      "content-type": "text/markdown; charset=utf-8",
      "x-markdown-tokens": String(estimateTokens(markdown)),
    },
  })
}
</code></pre>
<hr />
<h2 id="loopprevention">Loop prevention</h2>
<p>Self-fetching the same URL can recurse forever unless you guard it.</p>
<p>I add an internal header (<code>x-markdown-skip: 1</code>) on internal fetches and short-circuit middleware when it exists:</p>
<pre><code class="ts language-ts">if (request.headers.get("x-markdown-skip") === "1") {
  return NextResponse.next()
}
</code></pre>
<p>And in internal fetch:</p>
<pre><code class="ts language-ts">headers.set("x-markdown-skip", "1")
headers.set("accept", "text/html")
</code></pre>
<p>This gives a safe two-pass flow:</p>
<ul>
<li>external request asks for markdown</li>
<li>internal request asks for html with skip header</li>
</ul>
<p>I preferred an explicit skip header over heuristic checks because it is deterministic and easy to debug in logs when middleware chains grow.
This was a reliability decision more than an implementation detail. Middleware stacks evolve over time, and explicit control signals age better than implicit checks.</p>
<hr />
<h2 id="middlewareapi">Middleware API</h2>
<p>The package exports <code>withMarkdownForAgents()</code> so you can run standalone or wrap existing middleware.</p>
<pre><code class="ts language-ts">// proxy.ts (Next.js 16+) or middleware.ts (Next.js &lt;= 15)
import { withMarkdownForAgents } from "next-agent-md"

export default withMarkdownForAgents()

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
}
</code></pre>
<p>You can also compose with your own middleware:</p>
<pre><code class="ts language-ts">import { withMarkdownForAgents } from "next-agent-md"
import { myAuthMiddleware } from "./lib/auth"

export default withMarkdownForAgents(myAuthMiddleware, {
  contentSignal: { aiTrain: true, search: true, aiInput: false },
})
</code></pre>
<p>The <code>contentSignal</code> option emits a <code>Content-Signal</code> header for AI policy hints.
I exposed this as a wrapper API because most production apps already have auth, rewrites, or locale middleware. The goal was composition, not replacement.</p>
<hr />
<h2 id="strippingboilerplate">Stripping boilerplate</h2>
<p>Markdown is useful not just because it is shorter, but because it removes noise.</p>
<p>Agents do not need:</p>
<ul>
<li>nav bars</li>
<li>footers</li>
<li>sidebars</li>
<li>scripts/styles</li>
</ul>
<p>Because this runs on Edge, I avoided <code>jsdom</code> and used a lightweight regex strategy.</p>
<pre><code class="ts language-ts">const DEFAULT_STRIP_TAGS = [
  "nav",
  "header",
  "footer",
  "aside",
  "script",
  "style",
  "noscript",
  "iframe",
  "svg",
]
</code></pre>
<p>I also strip landmark roles like <code>navigation</code>/<code>banner</code> and HTML comments.</p>
<p>I considered DOM-based parsing, but Edge constraints pushed me toward smaller, browser-compatible code. So I chose a pragmatic middle ground: regex + role-based stripping. It is less theoretically perfect than a full parser, but much better for runtime footprint and cold-start behavior.</p>
<hr />
<h2 id="prebuildingstaticpages">Pre-building static pages</h2>
<p>Self-fetch + convert is fine for dynamic pages, but wasteful for static pages.</p>
<p>So I added a build step:</p>
<pre><code class="bash language-bash">npx next-agent-md build
</code></pre>
<p>This command is intentionally designed to run <strong>after</strong> <code>next build</code>. At that point Next.js has already materialized static HTML, so <code>next-agent-md build</code> can do pure transformation work with zero runtime ambiguity.</p>
<p>Under the hood, the command does three things:</p>
<ol>
<li>reads <code>.next/prerender-manifest.json</code> to discover which routes are truly static</li>
<li>maps each route to its generated HTML file inside <code>.next/server/...</code></li>
<li>converts those files to markdown and writes them to <code>public/.well-known/markdown/</code></li>
</ol>
<p>That output path matters because it is deployable static content, so your CDN can serve it directly without invoking any markdown conversion logic at request time.</p>
<p>After <code>next build</code>, it reads <code>.next/prerender-manifest.json</code>, finds static routes, converts their HTML once, and writes markdown to:</p>
<pre><code class="txt language-txt">public/.well-known/markdown/
  index.md
  about.md
  blog/
    hello-world.md
    getting-started.md
</code></pre>
<h3 id="fastpathatruntime">Fast path at runtime</h3>
<pre><code class="ts language-ts">const prebuilt = await fetchPrebuiltMarkdown(request)
if (prebuilt) {
  return new Response(prebuilt, {
    headers: {
      "content-type": "text/markdown; charset=utf-8",
      "x-markdown-tokens": String(estimateTokens(prebuilt)),
      "x-markdown-source": "prebuilt",
    },
  })
}

const html = await fetchPageHtml(request, skipHeader)
</code></pre>
<p>This split (prebuilt for static, self-fetch for dynamic) keeps runtime overhead low where it can be low, without dropping coverage for dynamic routes.
Architecturally, this became a two-lane system:</p>
<ul>
<li>build-time lane for static routes (cheap at runtime)</li>
<li>request-time lane for dynamic routes (always correct)</li>
</ul>
<p>That balance gave me good latency without sacrificing correctness.</p>
<p>It also made operations simpler: dynamic routes still work automatically, while static routes become precomputed artifacts you can inspect, diff, and debug in CI.</p>
<p>Wire it into build:</p>
<pre><code class="json language-json">{
  "scripts": {
    "build": "next build &amp;&amp; next-agent-md build"
  }
}
</code></pre>
<hr />
<h2 id="tokenestimation">Token estimation</h2>
<p>Each markdown response includes <code>x-markdown-tokens</code> so agents can budget context.</p>
<p>I used a practical approximation (<code>~1 token per 4 chars</code>) because <code>tiktoken</code> + WASM is not Edge-friendly in this setup.</p>
<pre><code class="ts language-ts">export function estimateTokens(text: string): number {
  return Math.ceil(text.length / 4)
}
</code></pre>
<p>This was another deliberate tradeoff: I wanted predictable, low-cost metadata over exact token accounting. The estimate is close enough for context budgeting, and cheap enough to compute on every request.</p>
<hr />
<h2 id="clisetup">CLI setup</h2>
<p>Setup is intentionally minimal:</p>
<pre><code class="bash language-bash">npm install next-agent-md
npx next-agent-md init
</code></pre>
<p><code>init</code> will:</p>
<ul>
<li>detect Next.js version</li>
<li>create <code>proxy.ts</code> for Next.js 16+ (<code>middleware.ts</code> for older versions)</li>
<li>avoid overwriting existing middleware</li>
<li>print test curl commands</li>
</ul>
<p>I spent extra time on <code>init</code> because integration friction usually kills small infrastructure tools. If setup is not one command, most people will never try it.</p>
<p>Example output:</p>
<pre><code class="txt language-txt">✔ Created proxy.ts

AI agents can now request Markdown from any page:
  curl -H "Accept: text/markdown" http://localhost:3000/
</code></pre>
<hr />
<h2 id="testitnow">Test it now</h2>
<p>You can test a live endpoint directly with curl:</p>
<pre><code class="bash language-bash">curl -si -H "Accept: text/markdown" https://fayeed.dev
</code></pre>
<p>If you’re testing locally, or on your own domain:</p>
<pre><code class="bash language-bash">curl -si -H "Accept: text/markdown" http://localhost:3000/
</code></pre>
<p>Look for:</p>
<ul>
<li><code>content-type: text/markdown</code></li>
<li><code>x-markdown-tokens</code></li>
<li><code>x-markdown-source</code> (when prebuilt markdown is used)</li>
</ul>
<hr />
<h2 id="edgeruntimeconstraints">Edge runtime constraints</h2>
<p>At request-time (Edge Runtime), no Node APIs:</p>
<ul>
<li>no <code>fs</code></li>
<li>no <code>child_process</code></li>
<li>no Node-only libs</li>
</ul>
<p>So runtime conversion code is Edge-safe, while build tooling (<code>next-agent-md build</code>) runs in Node and can use <code>fs</code> freely.</p>
<p>Separating Edge-time code from Node-time code became a core architectural boundary. Once I made that explicit in package exports, the implementation got much cleaner.</p>
<pre><code class="json language-json">{
  "exports": {
    ".": { "import": "./dist/edge.js" },
    "./config": { "import": "./dist/node-config.js" }
  }
}
</code></pre>
<hr />
<h2 id="whatsnext">What’s next</h2>
<p>A few improvements I still want:</p>
<ol>
<li><strong>Streaming conversion</strong> to reduce TTFB for large pages</li>
<li><strong>Better caching controls</strong> (<code>Cache-Control</code>, CDN behavior)</li>
<li><strong>More robust stripping</strong> via lightweight streaming HTML parsing</li>
</ol>
<p>If you want to try it:</p>
<ul>
<li>GitHub: <a href="https://github.com/fayeed/next-agent-md">fayeed/next-agent-md</a></li>
<li>npm install + init:</li>
</ul>
<pre><code class="bash language-bash">npm install next-agent-md
npx next-agent-md init
</code></pre>
<p>Thanks for reading. If you test this in production, I’d love feedback on edge cases.</p>]]></description><link>https://fayeed.dev/blog/building-next-agent-md</link><guid isPermaLink="true">https://fayeed.dev/blog/building-next-agent-md</guid><category><![CDATA[Next.js]]></category><category><![CDATA[AI Agents]]></category><category><![CDATA[Middleware]]></category><category><![CDATA[Markdown]]></category><category><![CDATA[LLM]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate></item><item><title><![CDATA[Using HTML in SVG (Wait, What?)]]></title><description><![CDATA[<p>A month back or so I saw Github launched a new feature called Github Readmes which allowed developers to build some amazing looking readmes to add to their profile. After seeing all the amazing Readmes I thought to myself this looks good I should make one too for myself. The initial readme was just a copy-paste from other profiles that I found interesting. After a few days later I thought that this looks too generic, so decided to build an SVG banner, here's what the banner looked like.</p>
<p><img src="https://raw.githubusercontent.com/fayeed/fayeed/f315032ff4b2367c10d78aab97449919dc2d0b3e/Banner.svg" alt="" /></p>
<p>As you can see this looks great, but then I started to wonder what if I can make a customizable Github banner with random Avatars, gradients &amp; stuff.</p>
<p>After doing some research I found that I could build an API that will serve the SVG, but as I started to build the API I found that the tags supported by the SVG are a little difficult to work with. So I wondered wouldn't it be good if I could use an HTML tag.</p>
<p>So I went off to google to find a solution and quickly found a post on MDN about <code>foreignObject</code> which allows the use of HTML tags inside SVG, though it is a little limited in styling.</p>
<blockquote>
  <p>The <foreignObject> SVG element includes elements from a different XML namespace. In the context of a browser, it is most likely (X)HTML.</p>
</blockquote>
<p>So this is how it works, you just need to add a new tag <code>foreignObject</code> inside which you need to add XHTML namespace, your SVG file would look something like this:</p>
<pre><code class="svg language-svg">&lt;svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"&gt;
  &lt;foreignObject x="20" y="20" width="160" height="160"&gt;
    &lt;style&gt;
      div {
        color: red;
        bacgroundColor: green;
      }
    &lt;/style&gt;
    &lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;
      Hello world
    &lt;/div&gt;
  &lt;/foreignObject&gt;
&lt;/svg&gt;
</code></pre>
<p>And that's it. I won't talk about how I build the Github Banner, but just by using the method I was able to build something like this:</p>
<p><img src="https://github-banner.vercel.app/api/social/?heading=Hi%2C+I+am+Fayeed+Pawaskar+-+%3Cbr+%2F%3E%0ACode+Tinkerer+%26+Software+Engineer&subheading=&social=%7B%22mail%22%3A%22fayeed%40live.com%22%2C%22linkedIn%22%3A%22fayeedpawaskar%22%2C%22github%22%3A%22%40fayeed%22%2C%22stackoverflow%22%3A%22%40fayeed%22%2C%22twitter%22%3A%22%40fayeedP%22%2C%22rss%22%3A%22fayeed.dev%2Frss.xml%22%7D&alignItems=flex-start&headingStyles=%7B%22fontSize%22%3A32%2C%22textAlign%22%3A%22start%22%2C%22color%22%3A%22%22%2C%22thirdDim%22%3Afalse%2C%22lineHeight%22%3A%22%22%2C%22fontWeight%22%3A%22700%22%7D&subheadingStyles=%7B%22fontSize%22%3A14%2C%22textAlign%22%3A%22start%22%2C%22color%22%3A%22%22%2C%22thirdDim%22%3Afalse%2C%22lineHeight%22%3A%22%22%2C%22fontWeight%22%3A%22500%22%7D&socialStyles=%7B%22fontSize%22%3A14%2C%22color%22%3A%22%22%2C%22iconColor%22%3A%22%22%2C%22lineHeight%22%3A%221%22%2C%22fontWeight%22%3A%22500%22%7D&background=%7B%22color%22%3A%22%22%2C%22image%22%3A%22%22%2C%22size%22%3A%22contain%22%2C%22repeat%22%3A%22no-repeat%22%2C%22position%22%3A%22center%22%7D&border=%7B%22size%22%3A0%2C%22color%22%3A%22%22%2C%22radius%22%3A8%7D&avatarStyles=%7B%22height%22%3A320%2C%22width%22%3A250%2C%22reverse%22%3Afalse%2C%22name%22%3A%22%22%2C%22hide%22%3Afalse%7D&gradient=true" width="100%" height="300" /></p>
<p>Check out the project below:</p>
<blockquote>
  <p><a href="https://github.com/fayeed/github-banner"><strong>fayeed/github-banner</strong></a></p>
  <p><small>Build custom Github banners for your Github profiles, to add some flair to your Readmes.</small></p>
</blockquote>
<p>Though the styling is limited at the moment for the banner because I just wanted to check the idea, I might add more styling options in the future for this, so stay tuned for more updates.</p>]]></description><link>https://fayeed.dev/blog/using-html-in-svg</link><guid isPermaLink="true">https://fayeed.dev/blog/using-html-in-svg</guid><category><![CDATA[foreignObject]]></category><category><![CDATA[HTML]]></category><category><![CDATA[SVG]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Thu, 27 Aug 2020 08:53:03 GMT</pubDate></item><item><title><![CDATA[Generating a sitemap for your static Nextjs website.]]></title><description><![CDATA[<p>I am going to keep this post short as I didn't have much time this week.</p>
<p>Sitemaps are very important for a website as they provide a guide or map for web crawlers like Google to easily find content &amp; index them easier, anything you want the search engines to find must be included in the sitemap &amp; anything not included is not indexed.</p>
<p>Creating a sitemap is easy but how would you do it if you have dynamic content like a blog post or maybe you have an e-commerce website.</p>
<p>Lucky for us, it's really simple I will go through the steps I went through while adding a sitemap to my site.</p>
<blockquote>
  <p><strong>Fun fact:</strong> You don't need a sitemap if your website is properly linked, doesn't have any dynamic content &amp; has less than 500 pages.</p>
  <p>Also, sitemaps cannot include more than 50,000 URLs.</p>
</blockquote>
<h2 id="whatisthepurpsoseofthesitemap">What is the purpsose of the sitemap?</h2>
<p>The Sitemap provides very helpful information to the crawlers like URL or location for the page, last modified date, how frequently the data is updated &amp; the priority for this page.</p>
<p>Here's the sitemap for my website to explain the structure in more detail:</p>
<pre><code class="xml language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev&lt;/loc&gt;
    &lt;lastmod&gt;2020-07-29&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev/blog&lt;/loc&gt;
    &lt;lastmod&gt;2020-07-29&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev/blog/building-my-personal-website-using-nextjs-ssg&lt;/loc&gt;
    &lt;lastmod&gt;2020-08-06&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev/blog/how-to-make-inline-links-in-flutter&lt;/loc&gt;
    &lt;lastmod&gt;2019-08-11&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev/blog/create-a-chat-app-in-minutes-in-flutter&lt;/loc&gt;
    &lt;lastmod&gt;2019-08-05&lt;/lastmod&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;https://fayeed.dev/projects&lt;/loc&gt;
    &lt;lastmod&gt;2020-07-29&lt;/lastmod&gt;
  &lt;/url&gt;
&lt;/urlset&gt;
</code></pre>
<p>Sitemaps only use six different type of tags <code>urlset</code>, <code>&lt;url&gt;</code>, <code>&lt;loc&gt;</code>, <code>&lt;lastmod&gt;</code>, <code>&lt;changefreq&gt;</code> &amp; <code>&lt;prority&gt;</code>.</p>
<h3 id="urlset">urlset</h3>
<p>This is the top most tag &amp; provides reference to the sitemap protocol to the search engines. All the url tags lives inside this tag.</p>
<h3 id="url">url</h3>
<p>Parent tag for each url &amp; all the remaining tags are children.</p>
<ul>
<li><code>&lt;loc&gt;</code> - This specifies the location for the each page &amp; must be start with a protocol like <code>http</code> or <code>https</code>.</li>
<li><code>&lt;lastmod&gt;</code> - This is a optional tag that specifies the last date this page was modified.</li>
<li><code>&lt;changefreq&gt;</code> - This is also a optional tag and its purpose it to show how frequently is this page content likely to change, think of reddit.</li>
<li><code>&lt;prority&gt;</code> - The priority of this url relative to others. Google acutally ignore this tag so need to use this.</li>
</ul>
<h2 id="howdoicreateasitemapformysite">How do I create a sitemap for my site?</h2>
<p>Well creating a sitemap is as easy as creating a new XML file in your project public/static root folder called <code>sitemap.xml</code> and follow the <code>xml</code> structure mentioned above.</p>
<p>You do also need to create a <code>robot.txt</code> file in the same folder &amp; add a link to your sitemap there, this will allow the crawlers to easily find the sitemap if it is somewhere else other than the root of the URL.</p>
<pre><code>User-agent: *
Sitemap: https://example.com/sitemap.xml
</code></pre>
<h2 id="howdoihandledynamiccontent">How do I handle dynamic content?</h2>
<p>If you have some form of dynamic content on your NextJs website like a blog, adding each URL to the sitemap can be kind of a pain. To easily resolve this we can create a helper function for this.</p>
<pre><code class="ts language-ts">import fs from "fs";
import { PostData } from "../types/post_data";

export const generateSitemap = async (posts: PostData[]) =&gt; {
  const sitemap = `
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;
  &lt;url&gt;
      &lt;loc&gt;${BASE_URL}&lt;/loc&gt;
  &lt;/url&gt;
  &lt;url&gt;
    &lt;loc&gt;${BASE_URL}/blog&lt;/loc&gt;
  &lt;/url&gt;
  ${[...posts]
    .map((post) =&gt; `&lt;url&gt;&lt;loc&gt;${post.canonicalUrl}&lt;/loc&gt;&lt;/url&gt;`)
    .join("")}
  &lt;url&gt;
    &lt;loc&gt;${BASE_URL}/projects&lt;/loc&gt;
  &lt;/url&gt;
&lt;/urlset&gt;`.replace(",", "");

  // writes sitemap.xml to public directory
  const path = `${process.cwd()}/public/sitemap.xml`;

  fs.writeFileSync(path, sitemap, "utf8");

  console.log(`generated sitemap`);

  return sitemap;
};
</code></pre>
<p>What this little helper function does it goes through, all the posts that we pass it, generates a sitemap string which we then write to our public folder using the <code>fs</code> module. In my case, I already had another helper function which gets all the markdown file &amp; all the props in it I just passed it to this function.</p>
<p>And then in your <code>getStaticProps</code> method in your landing page you can just call this helper function &amp; it will create the sitemap for you.</p>
<pre><code class="ts language-ts">export const getStaticProps = async () =&gt; {
  // gets all the markdown files &amp; returns the content in proper structure
  const posts = await loadBlogPosts();

  await generateSitemap(posts);
};
</code></pre>
<p>And don't forget to create <code>Robot.txt</code> file</p>
<pre><code>User-agent: *
Sitemap: https://example.com/sitemap.xml
</code></pre>]]></description><link>https://fayeed.dev/blog/generating-sitemap-for-your-nextjs-website</link><guid isPermaLink="true">https://fayeed.dev/blog/generating-sitemap-for-your-nextjs-website</guid><category><![CDATA[Personal website]]></category><category><![CDATA[SSG (Static Site Generation)]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[Sitemap]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Thu, 13 Aug 2020 13:02:02 GMT</pubDate></item><item><title><![CDATA[Building my personal website using Nextjs SSG (Static Site Generation)]]></title><description><![CDATA[<p>Recently I found myself with a lot of free time on my hand, so I took a look at the list of things I would like to complete in 2020 and found one thing that was there from 2019, my website. Here are my goals for the site:</p>
<ol>
<li>A blog to put my thoughts.</li>
<li>Dark mode support.</li>
<li>Faster load times.</li>
<li>A good SEO.</li>
</ol>
<p><strong>Before we get started, here's the complete TechStack that I used:</strong></p>
<ul>
<li>Next.js</li>
<li>React</li>
<li>SCSS</li>
<li>Typescript</li>
<li><a href="https://devii.dev">Devii Boilerplate</a> - I changed a few things to make it fit my needs.</li>
</ul>
<hr />
<h2 id="fasterloadtimesgoodseo">Faster load times &amp; good SEO</h2>
<p>Back in March Next.js released a new update v9.3, where they introduced a new feature SSG (Static Site Generation) which allowed Next.js users to generate Static sites from the Next.js projects, previously this was not possible if someone needs to build a Static site they had to rely on <a href="https://www.gatsbyjs.org/">Gatsby</a> for this or just use good old HTML &amp; CSS, which most people would say is still the best for your personal website.</p>
<p>But I still went with the Next.js because of these things:</p>
<ul>
<li>I like how Next.jS structures the project.</li>
<li>It's easy to build Blogs now that Next.js support SSG</li>
<li>I love ReactJS.</li>
</ul>
<p><strong>Faster load times</strong></p>
<p>Load times for the pages can be improved drastically just by serving static files over a CDN, and since Vercel also provides CDN, we can leverage that to get faster load times.</p>
<blockquote>
  <p>Pages that use Static Generation and assets (JS, CSS, images, fonts, etc) will automatically be served from the <a href="https://vercel.com/home">Vercel Smart</a> CDN, which is blazingly fast.</p>
</blockquote>
<p><strong>Loading SVG as React components</strong></p>
<p>To improve loading speed even more, I also converted SVG images to React Components using <a href="https://react-svgr.com/playground/">React SVGR</a> which reduces network request as the SVG is now rendered as part of the HTML.</p>
<p><strong>Loading fonts from same domain</strong></p>
<p>I was initially loading fonts from <a href="https://fonts.google.com/">Google fonts</a>, which was taking about <code>0.4s</code> to load the fonts from thier server because of the round trip, I easily resolved it just by moving the fonts to <code>static</code> folder &amp; then loading the fonts from the same domain.</p>
<p><strong>Good SEO</strong></p>
<p>NextJs by default provides good SEO because the content is available when search engines request pages to crawl unlike in client-side render web apps which have poor SEO.</p>
<p>SEO can be further improved by using a package like <a href="https://github.com/garmeeh/next-seo">NextSEO</a> or just by manually handling it by using <code>meta</code> tags, but I would recommend NextSEO as it provides a better syntax &amp; omits few redundant tags.</p>
<pre><code class="tsx language-tsx">import React from "react";
import { NextSeo } from "next-seo";

export default () =&gt; (
  &lt;&gt;
    &lt;NextSeo
      title="My awesome website"
      description="My awesome website description"
      canonical="https://example.com"
      openGraph={{
        url: "https://example.com",
        title: "Open Graph Title",
        description: "Open Graph Description",
        images: [
          {
            url: "https://www.example.ie/og-image-01.jpg",
            width: 800,
            height: 600,
            alt: "Og Image Alt",
          },
        ],
        site_name: "SiteName",
      }}
      twitter={{
        handle: "@fayeedP",
        site: "@fayeed.dev",
        cardType: "summary_large_image",
      }}
    /&gt;
  &lt;/&gt;
);
</code></pre>
<hr />
<h2 id="ablogtoputmythoughts">A blog to put my thoughts.</h2>
<p>I used to post on <a href="https://medium.com/@fayeed">Medium</a>, but I didn't like how once you post on Medium it becomes their IP, that's why I decided to make my own blog.</p>
<p>Since v9.3 Next.js introduced a new API <code>getStaticProps</code> &amp; <code>getServerSideProps</code>, here's what they do:</p>
<blockquote>
  <p><code>getStaticProps</code> (Static Generation): Fetch data at build-time.</p>
  <p><code>getStaticPaths</code> (Static Generation): Specify dynamic routes to prerender based on data.</p>
  <p><code>getServerSideProps</code> (Server-Side Rendering): Fetch data on each request.</p>
</blockquote>
<p>By using <code>getStaticProps</code> we can get the blog post data at runtime &amp; pass it as props to Component, which then later uses it to generate static pages.</p>
<p>Since I used <a href="https://devii.dev">devii</a> most of this was set up by default, so didn't need to do much work here, but I will still give a brief rundown of how it works.</p>
<p>All the markdown files live under the <code>md/blog</code> folder from the root directory of the project, each markdown must have these fields, the below example is from <a href="https://fayeed.dev/blog/create-a-chat-app-in-minutes-in-flutter">Create a chat app in minutes in Flutter</a>.</p>
<pre><code class="markdown language-markdown">title: Create a chat app in minutes in Flutter
subtitle: Finally a better way to build your chat functionality
published: true
datePublished: 1564943400249
author: Fayeed Pawaskar
authorPhoto: /profile.jpg
bannerPhoto: https://cdn-images-1.medium.com/max/2700/1*pYJQe00OSztV3p5gFeG0Cw.jpeg
canonicalUrl: https://fayeed.dev/blog/create-a-chat-app-in-minutes-in-flutter
tags:

- Dash chat
- Flutter
- Dart
- Chat App
</code></pre>
<p>Then in <code>getStaticProps</code> function, we get content of the markdown files using <code>gray-matter</code> and then properly structure it before passing it to the component as props.</p>
<p>This makes a very easy to add more articles later as you just have to write markdown and push and Nextjs takes care of it.</p>
<hr />
<h2 id="darkmodesupport">Darkmode support</h2>
<p>I initial used <a href="https://styled-components.com/">Styled Components</a> as it comes with default Theming support, which was working pretty great initially but when I deployed to the Vercel and it generated Static site I started facing some problems with, styles were not copied to the articles pages, it was working as expected on other pages but for some reason not on blogs.</p>
<p>I tried fixing this issue using <a href="https://styled-components.com/">Styled Components's</a> <code>ServerStyleSheet</code> Class, in <code>_document.ts</code> which collects all the styles from the parent <code>App</code> tag and pass it a <code>style</code> tag. This solved the problem but introduced a new one, whenever I go back to the index page from the blog post page it would completely break the styles.</p>
<pre><code class="tsx language-tsx">import Document from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =&gt;
        originalRenderPage({
          enhanceApp: (App) =&gt; (props) =&gt;
            sheet.collectStyles(&lt;App {...props} /&gt;),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          &lt;&gt;
            {initialProps.styles}
            {sheet.getStyleElement()}
          &lt;/&gt;
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}
</code></pre>
<p>Finally, I decided to move everything from <a href="https://styled-components.com/">Styled Components</a> to <code>SCSS</code> which solved all the problems but I would have loved to use Styled Components.</p>
<p>To support light &amp; dark mode I wrote a small react hook that saves current mode to <code>localStorage</code> and adds a new class to body tag <code>light-mode</code> or <code>dark-mode</code> depending on the current mode.</p>
<pre><code class="tsx language-tsx">  export const darkMode(initialValue: boolean) {
    const [darkmode, setDarkmode] = useState(initialValue);

    const toggle = (value: boolean) =&gt; {
      document.body.classList.remove(!value ? "dark-mode": "light-mode");
      document.body.classList.add(value ? "dark-mode": "light-mode");
    }

    useEffect(() =&gt; {
      const value = localStorage.getItem("darkmode");

      if (value) {
        toggle(value);
      } else {
        document.body.classList.add(initialValue ? "dark-mode": "light-mode");
      }
    }, []);

    const toggleDarkmode = () =&gt; {
      localStorage.setItem("darkmode", !darkmode);

      setDarkmode(!darkmode);

      toggle(!darkmode);
    }

    return {darkmode, toggleDarkmode};
  }
</code></pre>
<p>And then based on the <code>body</code> tag class, I would then change the styles for the components.</p>
<pre><code class="scss language-scss">body.light-mode {
  background-color: var(--light-body);
  color: var(--light-text);
}

body.dark-mode {
  background-color: var(--dark-body);
  color: var(--dark-text);
}
</code></pre>
<hr />
<p>Thanks for reading, if you have any questions or suggestions get in touch with me on <a href="https://twitter.com/fayeedP">twitter</a>.</p>]]></description><link>https://fayeed.dev/blog/building-my-personal-website-using-nextjs-ssg</link><guid isPermaLink="true">https://fayeed.dev/blog/building-my-personal-website-using-nextjs-ssg</guid><category><![CDATA[Personal website]]></category><category><![CDATA[SSG (Static Site Generation)]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Thu, 06 Aug 2020 06:47:24 GMT</pubDate></item><item><title><![CDATA[How to make inline links in Flutter.]]></title><description><![CDATA[<p>Have you wanted to add links, phone numbers, email in a block of text and make them clickable using a single Widget than I have just the thing for you? Recently I found myself in the same boat so I created a Widget called <code>flutter_parsed_text</code> .</p>
<blockquote>
  <p><a href="https://github.com/fayeed/flutter_parsed_text"><strong>fayeed/flutter<em>parsed</em>text</strong></a></p>
  <p><small>A Flutter package to parse text and extract parts using predefined types like url, phone and email and also supports Regex.</small></p>
</blockquote>
<p>So, In this tutorail, we are going to explore this widget and see how simple it is to create an Inline link in Flutter.</p>
<p>First, lets jus create a fresh flutter project using:</p>
<pre><code class="bash language-bash">flutter create flutter_parsed_text_example
</code></pre>
<p>Now that we have a project let’s add the dependency to your <code>pubspec.yaml</code> .</p>
<pre><code class="bash language-bash">url_launcher: ^5.0.3
flutter_parsed_text: ^1.2.0
</code></pre>
<p>We are almost ready to start, Let's add <code>ParsedText</code> to your starter code, you can add the text in <code>text</code> parameter that needs to be parsed. You can set alignment by setting <code>alignment</code> and also set a default style for the text which does not match the regex using <code>style</code> parameter.</p>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*S8AFQz0e9YYwfWicvoVidQ.png" alt="base app" >
</div>
<pre><code class="dart language-dart">class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'example_app',
      home: Scaffold(
      body: Container(
          child: ParsedText(
              alignment: TextAlign.start,
              text:
                  "[@michael:51515151] Hello this is an example of the ParsedText,
                    links like http://www.google.com or http://www.facebook.com are clickable
                    and phone number 444-555-6666 can call too. But you can also do more with this package,
                    for example Bob will change style and David too.\nAlso a US number example +1-(800)-831-1117.
                    foo@gmail.com And the magic number is 42! #flutter #flutterdev",
              style: TextStyle(
                fontSize: 24,
                color: Colors.black,
              ),
            ),
        ),
      )
    );
  }
}
</code></pre>
<p>As you can see from the above example we have phone numbers, links, emails and also hashtags too. Since this library supports build-in types for email, links, and email there's no need to implement but we will have to implement custom regex for hashtag we will see that later, for now, let's start with build-in types.</p>
<p>ParsedText has<code>parse</code> parameter which takes a <code>List&lt;MatchText&gt;</code> . This <code>MatchText</code> the class has 4 parameters <code>type</code> , <code>style</code> , <code>onTap</code> , <code>pattern</code> let's go over them one by one:</p>
<ul>
<li><p>type: Uses build-in types for “email”, “phone” and “url”.</p></li>
<li><p>style: Styles for the parsed text.</p></li>
<li><p>onTap: Fires a callback function when this MatchText is tapped on.</p></li>
<li><p>pattern: Custom regex to match text too.</p></li>
</ul>
<p>Let’s the code example of this three build in types:</p>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*sD-1dyozEdDI1ZsJHwg6mw.png" alt="with parsed text added" >
</div>
<pre><code class="dart language-dart">ParsedText(
  alignment: TextAlign.start,
  text:
      "[@michael:51515151] Hello this is an example of the ParsedText, links like http://www.google.com or http://www.facebook.com are clickable and phone number 444-555-6666 can call too. But you can also do more with this package, for example Bob will change style and David too.\nAlso a US number example +1-(800)-831-1117. foo@gmail.com And the magic number is 42! #flutter #flutterdev",
  parse: &lt;MatchText&gt;[
    MatchText(
        type: "email",
        style: TextStyle(
          color: Colors.red,
          fontSize: 24,
        ),
        onTap: (url) {
          launch("mailto:" + url);
        }),
    MatchText(
        type: "url",
        style: TextStyle(
          color: Colors.blue,
          fontSize: 24,
        ),
        onTap: (url) async {
          var a = await canLaunch(url);

          if (a) {
            launch(url);
          }
        }),
    MatchText(
        type: "phone",
        style: TextStyle(
          color: Colors.purple,
          fontSize: 24,
        ),
        onTap: (url) {
          launch("tel:" + url);
        }),
  ],
)
</code></pre>
<p>Let's also try to make hashtags clickable here’s how you do it. Just by overriding the <code>pattern</code> parameter, we can have a clickable hashtag like so:</p>
<pre><code class="dart language-dart">ParsedText(
  alignment: TextAlign.start,
  text:
      "[@michael:51515151] Hello this is an example of the ParsedText, links like http://www.google.com or http://www.facebook.com are clickable and phone number 444-555-6666 can call too. But you can also do more with this package, for example Bob will change style and David too.\nAlso a US number example +1-(800)-831-1117. foo@gmail.com And the magic number is 42! #flutter #flutterdev",
  parse: &lt;MatchText&gt;[
    /*other match_text objects*/,
    MatchText(
      pattern: r"\B#+([\w]+)\b",
      style: TextStyle(
        color: Colors.pink,
        fontSize: 24,
      ),
      onTap: (url) async {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            // return object of type Dialog
            return AlertDialog(
              title: new Text("Hashtag clicked"),
              content: new Text("$url clicked."),
              actions: &lt;Widget&gt;[
                // usually buttons at the bottom of the dialog
                new FlatButton(
                  child: new Text("Close"),
                  onPressed: () {},
                ),
              ],
            );
          },
        );
      })
  ],
)
</code></pre>
<p>Here’s the final output:</p>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/1144/1*PtyrjE25szhiBFk9r9Rqbg.jpeg" alt="finished app" >
</div>
<p>If you want a to take a look at the working example you take a look the example folder in the packages Github repository.</p>
<blockquote>
  <p><a href="https://github.com/fayeed/flutter_parsed_text/tree/master/example"><strong>fayeed/flutter<em>parsed</em>text</strong></a></p>
  <p><small>A new Flutter project. This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project.</small></p>
</blockquote>
<p>Thanks for reading.</p>]]></description><link>https://fayeed.dev/blog/how-to-make-inline-links-in-flutter</link><guid isPermaLink="true">https://fayeed.dev/blog/how-to-make-inline-links-in-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Parsed Text]]></category><category><![CDATA[Text]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Sat, 10 Aug 2019 18:30:00 GMT</pubDate></item><item><title><![CDATA[Create a chat app in minutes in Flutter]]></title><description><![CDATA[<p>Recently I started working on a Social Media app and needed to build a Chat UI for it, as I come from React Native background I usually use react-native-gifted-chat which is Chat UI library for React Native and it mostly takes care of everything. As I was unable to find a Chat UI library for Flutter I thought of creating one myself, which I released under the name Dash Chat.</p>
<blockquote>
  <p><a href="https://github.com/fayeed/dash_chat"><strong>fayeed/dash_chat</strong></a></p>
  <p><small>The most complete Chat UI for flutter Inspired by react-native-gifted-chat. Highly customizable and helps developing chat UI faster. </small></p>
</blockquote>
<p>As there are lots of chat tutorial available so I will keep this tutorial short and simple. We will just try to implement Firebase and Dash together. If you are ready to get your timer out because we will build this app in 15 minutes.</p>
<hr />
<h2 id="123herewego">1, 2, 3… here we go</h2>
<p>The first thing that we need do is create a Flutter Project you can use VsCode plugin for this or Flutter CLI and also setup Firebase. To set up firebase you can follow the guide below:</p>
<blockquote>
  <p><a href="https://medium.com/47billion/how-to-use-firebase-with-flutter-e4a47a7470ce"><strong>How to use Firebase with Flutter</strong></a></p>
</blockquote>
<p>Ok, great now that you have set up firebase we will add all the dependencies that we need for this tutorial in <code>pubspec.yaml.</code></p>
<pre><code class="yaml language-yaml">dependencies:
  flutter:
    sdk: flutter
  firebase_auth:
  cloud_firestore:
  firebase_storage:
  image_picker:
  shared_preferences:
  uuid:
  dash_chat: 1.0.4
</code></pre>
<p>In your <code>main.dart</code> the file you will have this as a starting point:</p>
<p><strong>Laying the Auth screen</strong></p>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*Is3yeNntnN9Mr-TRRFkNtQ.jpeg" alt="laying the auth screen" >
</div>
<p>We begin by creating the <code>MyHomePage</code> widget which we will use to get the <code>username</code> and also to authenticate the user on firebase anonymously.</p>
<pre><code class="dart language-dart">class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() =&gt; _MyHomePageState();
}
class _MyHomePageState extends State&lt;MyHomePage&gt; {
  GlobalKey&lt;FormState&gt; _formKey = GlobalKey&lt;FormState&gt;();
  TextEditingController _editingController = TextEditingController();
@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dash Chat"),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: &lt;Widget&gt;[
              TextFormField(
                controller: _editingController,
                decoration: InputDecoration(hintText: "Enter Name here..."),
                validator: (val) {
                  if (val.length &lt; 3) {
                    return "Name to short";
                  }
                  return null;
                },
              ),
              SizedBox(
                height: 40.0,
              ),
              FlatButton(
                child: Text("Next"),
                onPressed: () async {
                  // Check to see if username was correct if not dont authenticate
                  if (_formKey.currentState.validate()) {
                    try {
                      // Authenticate the user
                      await FirebaseAuth.instance.signInAnonymously();

                      // If authentication was sucessful move to chat screen.
                      // with username and uuid
                      Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) =&gt; ChatScreen(
                            username: _editingController.text,
                            uuid: Uuid().v4().toString(),
                          ),
                        ),
                      );
                    } catch (e) {
                      print(e);
                    }
                  }
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<hr />
<p><strong>Laying the Chat screen</strong></p>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*7lHy_0S4qkE3PwidIWw4VQ.png" alt="laying the chat screen" >
</div>
<p>Now that we have authenticated the user and we can start with the actual thing that you are here for, first lets set up the <code>ChatScreen.dart</code></p>
<pre><code class="dart language-dart">class ChatScreen extends StatefulWidget {
  final String username;
  final String uuid;

  ChatScreen({this.username, this.uuid});

  @override
  _ChatScreenState createState() =&gt; _ChatScreenState();
}

class _ChatScreenState extends State&lt;ChatScreen&gt; {
  ChatUser user = ChatUser();

  @override
  void initState() {
    user.name = widget.username;
    user.uid = widget.uuid;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dash Chat"),
      ),
      body: Container(),
    );
  }
}
</code></pre>
<p>Let’s remove the Container and replace with <code>StreamBuilder</code> so that we can listen to <code>Firestore</code> for changes. In your builder method, we are checking to see if we the stream has any data if not we show a <code>CircularProgressIndicator</code>and or else show the chat app.</p>
<pre><code class="dart language-dart">StreamBuilder(
          stream: Firestore.instance.collection('messages').snapshots(),
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return Center(
                child: CircularProgressIndicator(
                  valueColor: AlwaysStoppedAnimation&lt;Color&gt;(
                      Theme.of(context).primaryColor),
                ),
              );
            } else {
              return DashChat(
                user: user,
              );
            }
          },
),
</code></pre>
<hr />
<p>We will now start using implementing different features in the <code>dash_chat</code>widget. This widget has 52 properties that you can override to customize it however you want for this tutorial we will implement only a few of those properties but you can check the GitHub page for detail.</p>
<pre><code class="dart language-dart">List&lt;DocumentSnapshot&gt; items = snapshot.data.documents;
var messages =
    items.map((i) =&gt; ChatMessage.fromJson(i.data)).toList();

return DashChat(
         user: user,
         messages: messages,
       );
</code></pre>
<p>we will only override these properties in this tutorial:</p>
<ol>
<li><p>user (Require)</p></li>
<li><p>messages (Require)</p></li>
<li><p>inputDecoration</p></li>
<li><p>onSend</p></li>
<li><p>trailing</p></li>
</ol>
<p>We have already passed the user object to the <code>DashChat</code> widget now we need to pass a <code>List&lt;ChatMessage&gt;</code> to the DashChat for that, we will first need to convert the retrieved data from Firebase to <code>ChatMessage</code> we can do that above the widget using the <code>ChatMessage.fromJson</code> constructor.</p>
<p>After this, you will have a working chat app but we still have few things missing you cannot add message let do that now, to that you need to add a parameter called <code>onSend</code> which as a parameter gets the <code>ChatMessage</code> object back which we can then send to Firebase, make a note that we are running a transaction because since this is a 1-to-many chat so we might run it a lot of problems:</p>
<pre><code class="dart language-dart">onSend: (message) {
  var documentReference = Firestore.instance
      .collection('messages')
      .document(
          DateTime.now().millisecondsSinceEpoch.toString());

  Firestore.instance.runTransaction((transaction) async {
    await transaction.set(
      documentReference,
      message.toJson(),
    );
  });
},
</code></pre>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*K1jBodpy7GfPnxE4o_nX6w.png" alt="send message" >
</div>
<p>Now that we can successfully send messages we will move to upload images to Firebase Storage for this we will need to add a button in the input bar so that we can open gallery for this we can use to parameters first <code>leading</code> which will add widgets before the input and <code>trailling</code> which will add widgets after for this tutorial we will use <code>trailling</code>.</p>
<pre><code class="dart language-dart">trailing: &lt;Widget&gt;[
  IconButton(
    icon: Icon(Icons.photo),
    onPressed: uploadFile,
  )
],
</code></pre>
<p>Now that we have the icon we can use to call <code>uploadFile</code> method which will upload the file to Firebase to get the file from the gallery we will use <code>image_picker</code> package if you want to get the image from camera you change the source to <code>ImageSource.camera</code>.</p>
<pre><code class="dart language-dart">void uploadFile() async {
  File result = await ImagePicker.pickImage(
    source: ImageSource.gallery,
    imageQuality: 80,
    maxHeight: 400,
    maxWidth: 400,
  );

  if (result != null) {
    String id = Uuid().v4().toString();

    final StorageReference storageRef =
        FirebaseStorage.instance.ref().child("chat_images/$id.jpg");

    StorageUploadTask uploadTask = storageRef.putFile(
      result,
      StorageMetadata(
        contentType: 'image/jpg',
      ),
    );
    StorageTaskSnapshot download = await uploadTask.onComplete;

    String url = await download.ref.getDownloadURL();

    ChatMessage message = ChatMessage(text: "", user: user, image: url);

    var documentReference = Firestore.instance
        .collection('messages')
        .document(DateTime.now().millisecondsSinceEpoch.toString());

    Firestore.instance.runTransaction((transaction) async {
      await transaction.set(
        documentReference,
        message.toJson(),
      );
    });
  }
}
</code></pre>
<div style="text-align:center">
  <img src="https://cdn-images-1.medium.com/max/432/1*FbmWZeO9QRNgJXDGF1r2JQ.png" alt="Finished app" >
</div>
<p>Yay!!! Now you have a fully functional chat app which you can customize completely, we haven’t fully explored all the parameters in this tutorial but if like for me cover let me know in the comments in the meantime you can take a look at the docs in the link mentioned above.</p>
<p>Here’s the link to the Github repository:</p>
<blockquote>
  <p><a href="https://github.com/fayeed/dash_chat_example"><strong>fayeed/dash<em>chat</em>example</strong></a></p>
  <p><small>A new Flutter project. This project is a starting point for a Flutter application.</small></p>
</blockquote>
<p>Thank you for reading.</p>]]></description><link>https://fayeed.dev/blog/create-a-chat-app-in-minutes-in-flutter</link><guid isPermaLink="true">https://fayeed.dev/blog/create-a-chat-app-in-minutes-in-flutter</guid><category><![CDATA[Dash chat]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Chat App]]></category><dc:creator><![CDATA[Fayeed Pawaskar]]></dc:creator><pubDate>Sun, 04 Aug 2019 18:30:00 GMT</pubDate></item></channel></rss>