<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Developer on Travel</title>
    <description>The latest articles on Forem by Developer on Travel (@developerontravel).</description>
    <link>https://forem.com/developerontravel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3888373%2F55fe0303-760c-40ca-b9b7-f3e31a61fc71.png</url>
      <title>Forem: Developer on Travel</title>
      <link>https://forem.com/developerontravel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/developerontravel"/>
    <language>en</language>
    <item>
      <title>AI Travel Assistant Powered by Gemma 4; With Streaming, Image Input, and Visual Recommendation Cards</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Sat, 23 May 2026 06:42:24 +0000</pubDate>
      <link>https://forem.com/developerontravel/ai-travel-assistant-powered-by-gemma-4-with-streaming-image-input-and-visual-recommendation-cards-5hk1</link>
      <guid>https://forem.com/developerontravel/ai-travel-assistant-powered-by-gemma-4-with-streaming-image-input-and-visual-recommendation-cards-5hk1</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Planning a trip used to mean bouncing between five browser tabs — one for flights, one for hotels, one for itineraries, one for Reddit threads, and one you forgot you opened. I wanted to collapse that into a single conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma Travel Assistant&lt;/strong&gt; is an AI-powered chat app that helps you plan trips from scratch. Tell it your budget, your vibe, your dates. Ask follow-up questions. Upload a photo of somewhere you saw on Instagram and ask "where is this, and what should I do there?" It remembers everything you said earlier in the conversation and uses it to give you better answers.&lt;/p&gt;

&lt;p&gt;What makes it feel different from a plain chatbot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It doesn't just write paragraphs.&lt;/strong&gt; When Gemma recommends hotels or destinations, the app parses those recommendations out of the response and renders them as visual cards — name, location, type badge (hotel / destination / restaurant), star rating, price range. You can scan five options in three seconds instead of reading five bullet points.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Responses stream token by token.&lt;/strong&gt; You start reading the answer while Gemma is still writing it. For a full 5-day itinerary that can be 600+ words, this makes the experience feel instant instead of frozen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It understands images natively.&lt;/strong&gt; Drop in a photo — a landscape, a hotel lobby, a plate of food — and the model uses it as context. No extra vision pipeline, no OCR. Gemma 4 handles it directly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Example conversation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; Plan a 5-day trip to Kyoto in October, budget around $1500, I love temples and local food&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma:&lt;/strong&gt; Here's a day-by-day itinerary for Kyoto in October — peak foliage season, so I've planned around the best viewing spots...&lt;br&gt;
&lt;em&gt;(streams in, then suggestion cards appear below for ryokans and restaurants)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; &lt;em&gt;(uploads a photo of a bamboo forest)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma:&lt;/strong&gt; That's Arashiyama Bamboo Grove in western Kyoto. It's already on day 3 of your itinerary — here are the best times to visit to beat the crowds...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/mushahidmehdi/gemma-travel-assistant" rel="noopener noreferrer"&gt;https://github.com/mushahidmehdi/gemma-travel-assistant&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt;&lt;br&gt;
| Layer | Choice |&lt;br&gt;
|---|---|&lt;br&gt;
| Framework | Next.js 16 (App Router) |&lt;br&gt;
| Model | Gemma 4 31B Dense via OpenRouter |&lt;br&gt;
| Styling | Tailwind CSS |&lt;br&gt;
| Markdown | ReactMarkdown |&lt;br&gt;
| Icons | Lucide React |&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
├── app/
│   ├── api/chat/route.ts   # Streaming SSE proxy → OpenRouter
│   ├── layout.tsx
│   └── page.tsx            # Centered card layout
└── components/
    ├── ChatInterface.tsx   # Input, image upload, message list
    ├── ChatMessage.tsx     # Bubble renderer + suggestion parser
    └── SuggestionCard.tsx  # Hotel / destination / restaurant cards
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choosing the model
&lt;/h3&gt;

&lt;p&gt;I went with &lt;strong&gt;Gemma 4 31B Dense&lt;/strong&gt; (&lt;code&gt;google/gemma-4-31b-it&lt;/code&gt;). Here's why that specific model, not the others:&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;E2B / E4B&lt;/strong&gt; models are designed for edge and mobile — brilliant for offline use, but I needed server-grade reasoning quality for multi-day itineraries with budget constraints, visa tips, and local context. A 2B model can hallucinate confidently about things it doesn't know well.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;26B MoE&lt;/strong&gt; model is optimized for throughput. For a travel assistant where a single user sends a message and waits for the reply, throughput wasn't the bottleneck. Quality and coherence over a long conversation were.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;31B Dense&lt;/strong&gt; hits the right balance: strong enough to produce well-structured, accurate travel advice, consistent enough to reliably follow formatting instructions (more on that below), and available on OpenRouter's free tier so anyone can clone the repo and run it without a credit card.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;128K context window&lt;/strong&gt; was the other deciding factor. Planning a real trip is a long conversation. By the time you've discussed your budget, chosen a region, rejected two hotel options, added a day trip, and asked about visa requirements, you've accumulated thousands of tokens of context. Smaller context windows start dropping earlier constraints. With 128K, nothing gets forgotten.&lt;/p&gt;

&lt;h3&gt;
  
  
  Streaming the response
&lt;/h3&gt;

&lt;p&gt;The API route doesn't buffer — it pipes OpenRouter's SSE stream directly to the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/chat/route.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[DONE]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* skip malformed chunks */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain; charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the client, &lt;code&gt;ChatInterface&lt;/code&gt; reads the stream chunk by chunk and appends to the last message in state, so React re-renders progressively as tokens arrive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structured output via prompting
&lt;/h3&gt;

&lt;p&gt;I didn't use a formal structured output API. Instead, the system prompt tells Gemma to append a fenced &lt;code&gt;suggestions&lt;/code&gt; block at the end of any response that involves specific recommendations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;When&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;suggesting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;places,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;hotel/destination/restaurant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;recommendations&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;JSON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
suggestions&lt;br&gt;
[{&lt;br&gt;
  "name": "Nishiyama Onsen Keiunkan",&lt;br&gt;
  "location": "Yamanashi, Japan",&lt;br&gt;
  "type": "hotel",&lt;br&gt;
  "rating": 4.9,&lt;br&gt;
  "price": "$$$",&lt;br&gt;
  "description": "The world's oldest hotel, operating since 705 AD..."&lt;br&gt;
}]&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
typescript&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ChatMessage&lt;/code&gt; then does two things: strips that block from the visible text (so it doesn't appear as raw JSON in the bubble), and passes the parsed array to &lt;code&gt;SuggestionCard&lt;/code&gt; components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseSuggestions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/``&lt;/span&gt;&lt;span class="err"&gt;`
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```/);
  if (!match) return { text: content, suggestions: [] };

  const text = content.replace(/```&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;```/, '').trim();
  try {
    return { text, suggestions: JSON.parse(match[1]) };
  } catch {
    return { text: content, suggestions: [] }; // graceful fallback
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Gemma omits the block — for a conversational reply like "Great, let's add a day trip!" — the component falls through cleanly and just shows the text bubble. No crashes, no empty card rows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multimodal input
&lt;/h3&gt;

&lt;p&gt;Image uploads are encoded as base64 data URLs and injected into the last user message as an &lt;code&gt;image_url&lt;/code&gt; content block — the format OpenRouter and Gemma 4 expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isLastMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// base64 data URL&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemma 4's native vision understands the image without any preprocessing on my end — no external OCR, no separate vision model call. The model sees both the image and the conversation history and responds in context.&lt;/p&gt;




&lt;p&gt;Building this made me appreciate how much the &lt;strong&gt;context window size and multimodal capability&lt;/strong&gt; change what's actually possible in a single conversation. A travel assistant that forgets what you said three messages ago, or that can't look at a photo you found, is just a fancier search box. Gemma 4 31B makes it feel like talking to someone who's actually paying attention.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>I gave Hermes Agent a passport; it planned a better trip than I ever could</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Sat, 23 May 2026 06:04:25 +0000</pubDate>
      <link>https://forem.com/developerontravel/i-gave-hermes-agent-a-passport-it-planned-a-better-trip-than-i-ever-could-1beo</link>
      <guid>https://forem.com/developerontravel/i-gave-hermes-agent-a-passport-it-planned-a-better-trip-than-i-ever-could-1beo</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I'm a terrible travel planner. I open 40 browser tabs, spend three hours on TripAdvisor, forget half of what I found, and end up booking the first hotel that looked decent. Sound familiar?&lt;/p&gt;

&lt;p&gt;So when I heard about Hermes Agent — an open-source AI agent with 40+ built-in tools, real web search, and multi-step reasoning — my first thought wasn't &lt;em&gt;"I'll build a coding assistant"&lt;/em&gt; or &lt;em&gt;"I'll automate my workflows."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My first thought was: &lt;strong&gt;what if I just gave it a destination and let it plan the whole trip?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question became &lt;strong&gt;Voyage&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Voyage actually does
&lt;/h2&gt;

&lt;p&gt;You fill in a form: destination, dates, number of travellers, interests (culture, food, hiking, nightlife…), and budget style. Hit the button. Then watch.&lt;/p&gt;

&lt;p&gt;On the left side of the screen, you see Hermes Agent's thinking steps stream in live — &lt;em&gt;Searching for top attractions in Islamabad&lt;/em&gt;, &lt;em&gt;Reading local travel guides&lt;/em&gt;, &lt;em&gt;Planning day-by-day schedule&lt;/em&gt;. On the right, the finished itinerary appears once Hermes is done: a full day-by-day breakdown with morning, afternoon, and evening activities, accommodation picks, cost estimates, and local insider tips.&lt;/p&gt;

&lt;p&gt;Here's what it produced for a 5-day trip to Islamabad, Pakistan:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Day 1 — Arrival &amp;amp; the Old City&lt;/strong&gt;&lt;br&gt;
Morning: Arrive, check in near F-7 Markaz. Walk to Saidpur Village for breakfast.&lt;br&gt;
Afternoon: Faisal Mosque — largest in South Asia, stunning modernist architecture at the foot of the Margalla Hills.&lt;br&gt;
Evening: Lok Virsa Museum for Pakistani folk art and heritage. Dinner at Monal Restaurant for the city views.&lt;br&gt;
Local tip: Friday prayers fill Faisal Mosque — visit early morning for calm and photos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It didn't hallucinate. It knew that Monal has city views. It knew Saidpur Village is the good breakfast spot. It knew the Friday prayer timing. That's not training data — that's Hermes actually searching the web during the run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is an agent problem, not a chatbot problem
&lt;/h2&gt;

&lt;p&gt;Most "AI travel planners" are just ChatGPT with a nicer UI. You ask a question, it generates an answer from training data, you get a plausible-sounding but potentially stale itinerary.&lt;/p&gt;

&lt;p&gt;Hermes Agent is different because it &lt;em&gt;acts&lt;/em&gt;. It:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Searches the web for current information about the destination&lt;/li&gt;
&lt;li&gt;Reads actual travel articles and guides&lt;/li&gt;
&lt;li&gt;Cross-references multiple sources&lt;/li&gt;
&lt;li&gt;Reasons about your specific interests and budget&lt;/li&gt;
&lt;li&gt;Composes a structured plan from everything it found&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's five steps before it writes a single word of your itinerary. A chatbot does zero of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The technical side
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js 16 (App Router) + Tailwind CSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Agent&lt;/td&gt;
&lt;td&gt;Hermes Agent by Nous Research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;DeepSeek (via Hermes Agent — free tier)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming&lt;/td&gt;
&lt;td&gt;Server-Sent Events (SSE)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How Hermes Agent is wired in
&lt;/h3&gt;

&lt;p&gt;Hermes Agent has a &lt;code&gt;--quiet&lt;/code&gt; flag that makes it perfect for programmatic use — no spinner, no TUI chrome, just clean stdout. The Next.js API route spawns it as a subprocess:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hermesPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--quiet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--yolo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepseek-chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--provider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepseek&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;DEEPSEEK_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEEPSEEK_API_KEY&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pipe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we stream stdout back to the browser via Server-Sent Events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Browser  →  POST /api/plan
         ←  SSE stream of agent steps + final itinerary JSON
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt tells Hermes to output a structured JSON block at the end of its run. The API route watches for that block, parses it, and sends it to the frontend as the itinerary.&lt;/p&gt;

&lt;h3&gt;
  
  
  The prompt matters
&lt;/h3&gt;

&lt;p&gt;The travel prompt is detailed on purpose — it tells Hermes exactly what to research and what format to return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan a 5-day trip to Islamabad for 2 travellers.
Budget: moderate. Interests: culture, food, hiking.

Research the destination thoroughly:
- Best attractions matching their interests
- Recommended accommodation for their budget
- Local food and dining recommendations
- Practical tips and local customs
- Estimated daily costs

Output a JSON itinerary in this format: [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Giving Hermes a clear research mandate and a structured output format is what turns a vague "plan a trip" request into a reliable, parseable result.&lt;/p&gt;

&lt;h3&gt;
  
  
  DeepSeek + Hermes = surprisingly cheap
&lt;/h3&gt;

&lt;p&gt;Hermes Agent is model-agnostic. Switching providers is literally just &lt;code&gt;--model&lt;/code&gt; and &lt;code&gt;--provider&lt;/code&gt;. DeepSeek's free tier handled every test run without a single rate limit error. The whole app costs essentially nothing to run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three things that surprised me about Hermes Agent
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;--quiet&lt;/code&gt; mode is genuinely great for embedding.&lt;/strong&gt;&lt;br&gt;
Most CLI tools weren't designed to be spawned by another process. Hermes's quiet mode was — clean stdout, no escape codes, just the agent's output. Made integration trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The model-agnostic design is real, not marketing.&lt;/strong&gt;&lt;br&gt;
I tested with Anthropic first, switched to DeepSeek to save cost, and the only change was two flags. No code changes, no prompt changes, no adapter layer. It just worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It finds things I wouldn't think to search for.&lt;/strong&gt;&lt;br&gt;
I knew about Faisal Mosque. I didn't know about Saidpur Village for breakfast, or that Monal has the best city views, or the Friday prayer timing tip. Hermes found those because it was actually reading travel guides, not just recalling common knowledge.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/mushahidmehdi/hermes-travel-agent" rel="noopener noreferrer"&gt;github.com/mushahidmehdi/hermes-travel-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/NousResearch/hermes-agent" rel="noopener noreferrer"&gt;Hermes Agent&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;Free &lt;a href="https://platform.deepseek.com" rel="noopener noreferrer"&gt;DeepSeek API key&lt;/a&gt; (takes 2 minutes)&lt;/li&gt;
&lt;li&gt;Node.js 18+
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install Hermes Agent&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc

&lt;span class="c"&gt;# 2. Clone and run Voyage&lt;/span&gt;
git clone https://github.com/mushahidmehdi/hermes-travel-agent
&lt;span class="nb"&gt;cd &lt;/span&gt;hermes-travel-agent
npm &lt;span class="nb"&gt;install
echo&lt;/span&gt; &lt;span class="s2"&gt;"DEEPSEEK_API_KEY=your-key-here"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env.local
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;, pick a destination, and watch Hermes work.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd build next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live flight prices&lt;/strong&gt; — Hermes has browser tools. Pointing it at Google Flights for real pricing is very doable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative planning&lt;/strong&gt; — A "refine this day" button that sends a follow-up query to Hermes with the existing itinerary as context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shareable itineraries&lt;/strong&gt; — Persist the JSON output so you can send a link to your travel partner.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The thing that stuck with me building this: Hermes Agent isn't just a better chatbot. It's a different kind of tool. Giving it a task and watching it actually &lt;em&gt;do the work&lt;/em&gt; — search, read, reason, plan — feels genuinely different from asking a language model to generate an answer.&lt;/p&gt;

&lt;p&gt;Give it a passport. See what it does.&lt;/p&gt;

&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/hermes"&gt;Hermes Agent Challenge&lt;/a&gt;. Repo is above — if you build on it or fork it, I'd love to see what you make.&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
    </item>
    <item>
      <title>I Built a Travel Assistant with Gemini 2.5 Flash: Here's How</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Sat, 23 May 2026 04:52:02 +0000</pubDate>
      <link>https://forem.com/developerontravel/i-built-a-travel-assistant-with-gemini-25-flash-heres-how-gcg</link>
      <guid>https://forem.com/developerontravel/i-built-a-travel-assistant-with-gemini-25-flash-heres-how-gcg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-io-writing-2026-05-19"&gt;Google I/O Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google I/O 2026 dropped a lot of announcements, but the one that made me immediately open my code editor was &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt;. Faster, smarter, and free to start — no credit card needed.&lt;/p&gt;

&lt;p&gt;So I built something real with it: a &lt;strong&gt;conversational travel assistant&lt;/strong&gt; that answers questions about hotels, visas, itineraries, and local tips. In this post I'll walk you through every step so you can build it too.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Gemini 2.5 Flash?
&lt;/h2&gt;

&lt;p&gt;Announced at Google I/O 2026, Gemini 2.5 Flash is Google's fastest model in the 2.5 family — optimized for low latency while still being genuinely capable. It supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1M token context window&lt;/strong&gt; — enough for very long conversations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System instructions&lt;/strong&gt; — give the model a persistent role and persona&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-turn chat&lt;/strong&gt; — native conversation history support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1,500 free requests/day&lt;/strong&gt; via Google AI Studio — no credit card required&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;A Next.js app with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Next.js Route Handler&lt;/strong&gt; as a secure proxy to the Gemini API (your API key never reaches the browser)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;React chat UI&lt;/strong&gt; with suggestion chips, conversation history, typing indicator, and proper markdown rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gemini-travel-assistant/
├── app/
│   ├── api/chat/route.ts   ← Gemini API proxy
│   ├── page.tsx            ← main page
│   └── layout.tsx
├── components/
│   └── ChatWidget.tsx      ← chat UI
└── .env.local              ← your API key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Create the Project
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest gemini-travel-assistant &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--typescript&lt;/span&gt; &lt;span class="nt"&gt;--tailwind&lt;/span&gt; &lt;span class="nt"&gt;--app&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;gemini-travel-assistant
npm &lt;span class="nb"&gt;install&lt;/span&gt; @google/generative-ai react-markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Get Your Free API Key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://aistudio.google.com" rel="noopener noreferrer"&gt;aistudio.google.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign in with Google → click &lt;strong&gt;Get API Key&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;.env.local&lt;/code&gt; in your project root:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_key_here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;No billing setup needed.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 3: The API Route
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;app/api/chat/route.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenerativeAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/generative-ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;genAI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenerativeAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are a friendly and knowledgeable travel assistant powered by Gemini 2.5 Flash.
You help travelers with:
- Hotel and accommodation recommendations
- Travel tips and itineraries
- Visa and entry requirements
- Budget planning
- Local culture, food, and activities
- Best times to visit destinations

Keep your answers concise, practical, and helpful. Use bullet points when listing multiple items.
If you don't know something, say so honestly.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Message is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;genAI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGenerativeModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-2.5-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;systemInstruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Gemini API error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to get response from Gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two things to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;systemInstruction&lt;/code&gt;&lt;/strong&gt; gives Gemini a persistent role across the entire conversation without repeating it every turn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;history&lt;/code&gt;&lt;/strong&gt; is sent by the client on every request — the Gemini API is stateless, so you own the conversation history&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Step 4: The Chat Component
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;components/ChatWidget.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactMarkdown&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-markdown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SUGGESTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Best hotels in Tokyo under $100?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Visa requirements for Pakistan to Japan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7-day itinerary for Paris&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Best time to visit Bali&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ChatWidget&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bottomRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bottomRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userMessage&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedMessages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;history&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;updatedMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;}]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;updatedMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sorry, something went wrong. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col h-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 overflow-y-auto p-4 space-y-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col items-center justify-center h-full text-center gap-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-5xl mb-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;✈️&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-semibold text-gray-700"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Ask me anything about travel
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-400 text-sm mt-1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Powered by Gemini 2.5 Flash
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 sm:grid-cols-2 gap-2 w-full max-w-lg"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SUGGESTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
                  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-left text-sm bg-blue-50 hover:bg-blue-100 text-blue-700 rounded-xl px-4 py-3 transition-colors border border-blue-100"&lt;/span&gt;
                &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
            &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`flex &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;justify-end&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;justify-start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white text-xs font-bold mr-2 shrink-0 mt-1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                G
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`max-w-[75%] rounded-2xl px-4 py-3 text-sm leading-relaxed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
                &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-blue-600 text-white rounded-br-sm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bg-gray-100 text-gray-800 rounded-bl-sm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
              &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReactMarkdown&lt;/span&gt;
                  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-2 last:mb-0"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"list-disc pl-4 mb-2 space-y-1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ol&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"list-decimal pl-4 mb-2 space-y-1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-semibold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-200 rounded px-1 text-xs font-mono"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
                  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ReactMarkdown&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-start"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-8 h-8 rounded-full bg-blue-600 flex items-center justify-center text-white text-xs font-bold mr-2 shrink-0"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              G
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-100 rounded-2xl rounded-bl-sm px-4 py-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-1 items-center h-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:0ms]"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:150ms]"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:300ms]"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bottomRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"border-t border-gray-200 p-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;
          &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-2"&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Ask about hotels, visas, itineraries..."&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 border border-gray-200 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"&lt;/span&gt;
            &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
            &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-600 hover:bg-blue-700 disabled:opacity-40 text-white rounded-xl px-5 py-3 text-sm font-medium transition-colors"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Send
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The key pattern: on every request we rebuild the &lt;code&gt;history&lt;/code&gt; array from local state and send it to the server. This is how Gemini maintains context — it's stateless on the API side, so you own the conversation history.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 5: The Main Page
&lt;/h2&gt;

&lt;p&gt;Replace &lt;code&gt;app/page.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ChatWidget&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ChatWidget&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full max-w-2xl bg-white rounded-3xl shadow-2xl overflow-hidden flex flex-col h-[85vh]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-600 px-6 py-4 flex items-center gap-3"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;✈️&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-white font-bold text-lg leading-tight"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Gemini Travel Assistant
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-blue-200 text-xs"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Powered by Gemini 2.5 Flash · Google I/O 2026
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ml-auto flex items-center gap-1.5"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-2 h-2 bg-green-400 rounded-full animate-pulse"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-blue-200 text-xs"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Online&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 overflow-hidden"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChatWidget&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Run It
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt; — that's it. No Docker, no database, no paid tier.&lt;/p&gt;


&lt;h2&gt;
  
  
  My Take on Gemini 2.5 Flash
&lt;/h2&gt;

&lt;p&gt;After building with it, here's what stood out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What impressed me:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; — responses feel near-instant even with a full conversation history attached&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System instructions are rock solid&lt;/strong&gt; — the model stays in its travel assistant persona throughout, never drifting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The free tier is real&lt;/strong&gt; — 1,500 requests/day is enough to build, demo, and show off a finished app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown output&lt;/strong&gt; — it naturally formats responses with bold text, bullet points, and headers without you asking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to watch:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API is stateless — you manage history yourself. It's clean architecturally but easy to miss on your first build&lt;/li&gt;
&lt;li&gt;Double-check the model ID in the SDK docs as they evolve quickly — &lt;code&gt;gemini-2.5-flash&lt;/code&gt; is current as of I/O 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, Gemini 2.5 Flash is the most accessible entry point into production-quality AI I've used. Zero to a working app in under an hour, for free. That's what impressed me most at Google I/O 2026.&lt;/p&gt;


&lt;h2&gt;
  
  
  Full Source Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mushahidmehdi" rel="noopener noreferrer"&gt;
        mushahidmehdi
      &lt;/a&gt; / &lt;a href="https://github.com/mushahidmehdi/gemini-travel-assistant" rel="noopener noreferrer"&gt;
        gemini-travel-assistant
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Google I/O 2026 dropped a lot of announcements, but the one that made me immediately open my code editor was **Gemini 2.5 Flash**. Faster, smarter, and free to start — no credit card needed.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
✈️ Gemini Travel Assistant&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A conversational travel assistant built with &lt;strong&gt;Next.js&lt;/strong&gt; and &lt;strong&gt;Gemini 2.5 Flash&lt;/strong&gt; — answers questions about hotels, visas, itineraries, and local tips.&lt;/p&gt;

&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/google-io-writing-2026-05-19" rel="nofollow"&gt;Google I/O 2026 Writing Challenge&lt;/a&gt; on DEV.&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;💬 Multi-turn conversation with full history&lt;/li&gt;
&lt;li&gt;🗺️ Answers about hotels, visas, itineraries, budgets, and more&lt;/li&gt;
&lt;li&gt;⚡ Powered by Gemini 2.5 Flash — fast and free to start&lt;/li&gt;
&lt;li&gt;🔒 API key stays server-side via Next.js Route Handler proxy&lt;/li&gt;
&lt;li&gt;📝 Markdown responses rendered cleanly in the UI&lt;/li&gt;
&lt;/ul&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tech Stack&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://nextjs.org/" rel="nofollow noopener noreferrer"&gt;Next.js 16&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;App Router + API Route Handlers&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://aistudio.google.com/" rel="nofollow noopener noreferrer"&gt;Gemini 2.5 Flash&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;AI via &lt;code&gt;@google/generative-ai&lt;/code&gt;&lt;br&gt;
&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://tailwindcss.com/" rel="nofollow noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/remarkjs/react-markdown" rel="noopener noreferrer"&gt;react-markdown&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;Render Gemini's markdown output&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;br&gt;
&lt;/p&gt;


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1. Clone the repo&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;git clone https://github.com/mushahidmehdi/gemini-travel-assistant.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; gemini-travel-assistant&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;2. Install dependencies&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;3. Get a free Gemini API key&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Go to &lt;a href="https://aistudio.google.com" rel="nofollow noopener noreferrer"&gt;aistudio.google.com&lt;/a&gt;, sign in with Google, and click &lt;strong&gt;Get API Key&lt;/strong&gt;. No credit card required — 1,500…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mushahidmehdi/gemini-travel-assistant" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;





&lt;p&gt;&lt;em&gt;Built for the Google I/O 2026 Writing Challenge on DEV.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>googleiochallenge</category>
      <category>devchallenge</category>
      <category>gemini</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Walk 1 Minute to Chimelong Paradise: The Case for the Closest Hotels</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Fri, 22 May 2026 17:20:22 +0000</pubDate>
      <link>https://forem.com/developerontravel/walk-1-minute-to-chimelong-paradise-the-case-for-the-closest-hotels-250i</link>
      <guid>https://forem.com/developerontravel/walk-1-minute-to-chimelong-paradise-the-case-for-the-closest-hotels-250i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1626012782480-40331885734d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxDaGltZWxvbmclMjBQYXJhZGlzZSUyMEhlZmVpfGVufDB8MHx8fDE3Nzk0NzA0MjJ8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1626012782480-40331885734d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxDaGltZWxvbmclMjBQYXJhZGlzZSUyMEhlZmVpfGVufDB8MHx8fDE3Nzk0NzA0MjJ8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Chimelong Paradise — Amusement Park" width="1080" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only real question near Chimelong Paradise is whether you value proximity over everything else. Eight hotels cluster within a 2-minute walk of the park gates — that's not a commute, that's a hallway. Staying here means you can roll out of bed, cross the street, and be on a ride before the morning crowds thicken. Check &lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/chimelong-paradise/" rel="noopener noreferrer"&gt;tonight's rates for hotels around Chimelong Paradise&lt;/a&gt; to see if the convenience fits your budget.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're walking everywhere
&lt;/h3&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/xi-yue-themed-apartment-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Xi yue themed apartment hotel&lt;/strong&gt;&lt;/a&gt;. At 0.2 km — roughly a 1-minute walk — it's the closest option to the park entrance. The 2-star rating means no frills, but when your day starts with roller coasters and ends with fireworks, you don't need a lobby bar. You need a bed 60 seconds from the gate.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're travelling with family
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatnlpj4t3nrnci60xnle.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fatnlpj4t3nrnci60xnle.jpg" alt="Chimelong Paradise — Amusement Park" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/dongshan-jinluanwan-international-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Hip-hop Parent-child Holiday Hotel Apartment&lt;/strong&gt;&lt;/a&gt;. It's the only 3-star option within that 0.2 km cluster, and the name tells you exactly who it's for. The extra star usually translates to more space and soundproofing — useful when kids crash early and you're not ready to sleep. The 2-minute walk means no one's complaining about tired legs before you even reach the park.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're on a budget
&lt;/h3&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/zheng-fei-sunshine-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Zheng Fei Sunshine Hotel&lt;/strong&gt;&lt;/a&gt;. Among the 2-star properties within the same 0.2 km radius, this one consistently offers the lowest rates without sacrificing the doorstep location. You're paying for the address, not the room — and that's exactly the right trade-off when the park is your destination, not the hotel.&lt;/p&gt;

&lt;p&gt;If the 2-minute walk is your non-negotiable, any of these three works. Chimelong Paradise covers over 600,000 square meters — you'll walk plenty inside the park. Save your energy for the rides, not the commute.&lt;/p&gt;

&lt;h3&gt;
  
  
  What travellers ask
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Are there budget options near Chimelong Paradise?&lt;/strong&gt;Yes — seven 2-star hotels sit within a 2-minute walk of the park. &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/zheng-fei-sunshine-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Zheng Fei Sunshine Hotel&lt;/strong&gt;&lt;/a&gt; typically offers the lowest rates in that cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the closest hotel to Chimelong Paradise?&lt;/strong&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/xi-yue-themed-apartment-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Xi yue themed apartment hotel&lt;/strong&gt;&lt;/a&gt; is the closest at 0.2 km — a 1-minute walk to the park gates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worth a detour
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Nancun Wanbo&lt;/strong&gt; — place, 1.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shaxi&lt;/strong&gt; — place, 3.4 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shiguanglu&lt;/strong&gt; — place, 3.6 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flower City Square&lt;/strong&gt; — Historical Site, 12.0 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guangdong Museum&lt;/strong&gt; — Museum, 12.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guangzhou Opera House&lt;/strong&gt; — Theater, 12.1 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/chimelong-paradise/" rel="noopener noreferrer"&gt;Browse 8 hotels with up-to-date rates near Chimelong Paradise&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>chimelongparadise</category>
      <category>hotelsnearchimelongparadise</category>
      <category>amusementparkhotels</category>
      <category>hefei</category>
    </item>
    <item>
      <title>Book Smart: Hotels Near Shangxiajiu Pedestrian Street</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Fri, 22 May 2026 09:04:46 +0000</pubDate>
      <link>https://forem.com/developerontravel/book-smart-hotels-near-shangxiajiu-pedestrian-street-ec3</link>
      <guid>https://forem.com/developerontravel/book-smart-hotels-near-shangxiajiu-pedestrian-street-ec3</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F4%2F41%2FShangxj.jpg%2F1024px-Shangxj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F4%2F41%2FShangxj.jpg%2F1024px-Shangxj.jpg" alt="Shangxiajiu Pedestrian Street — Historical Site" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7005svito1e36enilq68.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7005svito1e36enilq68.JPG" alt="Shangxiajiu Pedestrian Street — Historical Site" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1567991964677-38d6cb81637a%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTaGFuZ3hpYWppdSUyMFBlZGVzdHJpYW4lMjBTdHJlZXQlMjBIZWZlaXxlbnwwfDB8fHwxNzc5NDQwNjg1fDA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1567991964677-38d6cb81637a%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTaGFuZ3hpYWppdSUyMFBlZGVzdHJpYW4lMjBTdHJlZXQlMjBIZWZlaXxlbnwwfDB8fHwxNzc5NDQwNjg1fDA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Shangxiajiu Pedestrian Street — Historical Site" width="1080" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shangxiajiu Pedestrian Street in Hefei is less a tourist landmark and more a living artery of the city — a dense stretch of shops, street food stalls, and local commerce that hums from late morning until well past dark. The real draw isn't a single monument; it's the chaos of bargaining for dried goods, the smell of frying scallion pancakes, and the way the crowd thickens as evening sets in. Sleeping close means you can dip in and out of that energy without fighting traffic, which is why you should check &lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/shangxiajiu-pedestrian-street/" rel="noopener noreferrer"&gt;real-time prices for Shangxiajiu Pedestrian Street hotels&lt;/a&gt; before committing to anything farther out.&lt;/p&gt;

&lt;h3&gt;
  
  
  The place
&lt;/h3&gt;

&lt;p&gt;Shangxiajiu Pedestrian Street isn't a museum — it's a working commercial district that happens to be historic. The buildings along it date back to the late Qing dynasty and early Republic era, with their distinctive arcaded facades (called &lt;em&gt;qilou&lt;/em&gt;) providing shade for the vendors underneath. Locals come here for cheap clothing, electronics accessories, and the kind of snacks you eat standing up: fish balls, rice noodle rolls, sugarcane juice pressed on the spot.&lt;/p&gt;

&lt;p&gt;Peak hours run from 4pm to 8pm, when the street is shoulder-to-shoulder and the fluorescent signs flicker on. Arrive before 10am and you'll see shopkeepers hosing down the pavement, rolling up metal shutters, and setting out buckets of live seafood. That's the better window for photography or just moving at your own pace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the hotel matters here
&lt;/h3&gt;

&lt;p&gt;This isn't a landmark you "visit" for an hour — people wander through, loop back, stop for tea, then wander again. A hotel within a 2-minute walk means you can retreat when the crowd gets too thick, drop off shopping bags, and re-enter without losing momentum.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hotels worth your attention
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/baowan-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Baowan Hotel&lt;/strong&gt;&lt;/a&gt; sits on the doorstep — under 100 meters from the pedestrian street itself. At 2-star level, you're not paying for amenities; you're paying for the ability to step out your door and be in the middle of it within 30 seconds. This is the pick for solo travellers or couples who plan to spend minimal time in the room.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/jiangyue-hotel-changshou-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;Jiangyue Hotel Changshou Branch&lt;/strong&gt;&lt;/a&gt; is the only 3-star option in the immediate cluster, at 0.1 km (roughly a one-minute walk). The step up in star rating here matters: better soundproofing, which helps when the street noise carries into the night. If you want proximity without hearing every scooter horn, this is the compromise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/home-inn-guangzhou-hualin-international-jade-city/" rel="noopener noreferrer"&gt;&lt;strong&gt;Homeinn Hotel (Guangzhou Yong Qing Fang Hualin Temple Subway Station)&lt;/strong&gt;&lt;/a&gt; at 0.2 km is a chain property that delivers exactly what the brand promises — clean, predictable, no surprises. The subway station connection in the name is the real value: you can walk to the pedestrian street in one minute and be on a train to other parts of Hefei in under five. Good for travellers combining the street with other city sights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/7days-inn-guangzhou-shangxia-jiu-rd-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;7Days Inn Guangzhou Shangxiajiu&lt;/strong&gt;&lt;/a&gt; at 0.2 km (two-minute walk) is the budget pick with the most direct name recognition. The trade-off is straightforward: the lowest price in the cluster, but the rooms are compact and the walls are thin. Worth it if you're backpacking or just need a bed for the night.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical reality
&lt;/h3&gt;

&lt;p&gt;Weekends draw families from across Hefei, which means the street is at its most crowded Saturday afternoon and Sunday evening. Tuesday through Thursday mornings are the quietest window — you'll have room to actually see the building facades. The nearest subway station (Changshou Road) is a 5-minute walk from the pedestrian street entrance, and a single ride covers most of the city centre.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before you book — common questions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What is the cheapest hotel near Shangxiajiu Pedestrian Street?&lt;/strong&gt;Among the verified options, &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/7days-inn-guangzhou-shangxia-jiu-rd-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;7Days Inn Guangzhou Shangxiajiu&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/motel-168-guangzhou-hualin-international-jade-city/" rel="noopener noreferrer"&gt;&lt;strong&gt;Motel 168 (Guangzhou Hualin International Jade City)&lt;/strong&gt;&lt;/a&gt; both sit at the 2-star budget tier, 0.2 km (two-minute walk) from the street. Exact rates fluctuate, but these are consistently the lowest-priced entries in the cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does it take to walk from the nearest hotel to Shangxiajiu Pedestrian Street?&lt;/strong&gt;&lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/baowan-hotel/" rel="noopener noreferrer"&gt;&lt;strong&gt;Baowan Hotel&lt;/strong&gt;&lt;/a&gt; is the closest — under 100 meters, which is roughly a 30-second walk. &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/jiangyue-hotel-changshou-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;Jiangyue Hotel Changshou Branch&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://myservice.pk/en/hotels/cn/guangzhou/yuexiangjia-apartment-guangzhou-yongqingfang-changshou-road-subway-station-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;Yuexiangjia Apartment&lt;/strong&gt;&lt;/a&gt; are both 0.1 km away, about one minute on foot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the best 4 or 5-star hotel near Shangxiajiu Pedestrian Street?&lt;/strong&gt;None of the verified hotels within 200 meters of Shangxiajiu Pedestrian Street are 4 or 5-star. The closest higher-rated options are located further out — you would need to check a broader search to find luxury properties within a reasonable distance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Around the area
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hualinsi Buddhist Temple&lt;/strong&gt; — place, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yide Lu&lt;/strong&gt; — place, 0.7 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sacred Heart Cathedral&lt;/strong&gt; — Church, 1.0 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;China Import &amp;amp; Export Fair Complex&lt;/strong&gt; — business center, 1.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onelink Plaza&lt;/strong&gt; — place, 1.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beijing Road Pedestrian Street&lt;/strong&gt; — Historical Site, 2.2 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/shangxiajiu-pedestrian-street/" rel="noopener noreferrer"&gt;See what 8 hotels near Shangxiajiu Pedestrian Street are charging tonight&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>shangxiajiupedestrianstreet</category>
      <category>hotelsnearshangxiajiupedestria</category>
      <category>historicalsitehotels</category>
      <category>hefei</category>
    </item>
    <item>
      <title>The park where Shenzhen's pulse slows to a stroll — your lodging options within 300 metres</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 21:08:49 +0000</pubDate>
      <link>https://forem.com/developerontravel/the-park-where-shenzhens-pulse-slows-to-a-stroll-your-lodging-options-within-300-metres-m22</link>
      <guid>https://forem.com/developerontravel/the-park-where-shenzhens-pulse-slows-to-a-stroll-your-lodging-options-within-300-metres-m22</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F881mseievvuiz9pg819l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F881mseievvuiz9pg819l.jpg" alt="Shenzhen People's Park — Park" width="800" height="406"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1644994576908-01372446a28e%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTaGVuemhlbiUyMFBlb3BsZSUyN3MlMjBQYXJrJTIwSGVmZWl8ZW58MHwwfHx8MTc3OTM5NzcyOXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1644994576908-01372446a28e%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTaGVuemhlbiUyMFBlb3BsZSUyN3MlMjBQYXJrJTIwSGVmZWl8ZW58MHwwfHx8MTc3OTM5NzcyOXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Shenzhen People's Park — Park" width="1080" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shenzhen People's Park in Hefei isn't the kind of place you rush through. At 0.2 km from the nearest entrance, the &lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/shenzhen-peoples-park/" rel="noopener noreferrer"&gt;what's open and at what price near Shenzhen People's Park&lt;/a&gt; list shows eight hotels clustered within a three-minute radius — a density that makes sense only once you've seen how the park works.&lt;/p&gt;

&lt;h3&gt;
  
  
  The place
&lt;/h3&gt;

&lt;p&gt;Shenzhen People's Park is a green lung in central Hefei, but it's not a passive one. By late afternoon, the walking paths fill with office workers stretching their commute, elderly couples practising tai chi in the shade, and the occasional vendor selling sugar-roasted chestnuts from a cart. The lake at its centre draws a steady current of strollers — not tourists with cameras, but locals who know exactly which bench gets the best breeze at 5pm.&lt;/p&gt;

&lt;p&gt;This isn't a landmark you visit once. It's a place you integrate into a daily rhythm. The question isn't whether you'll go — it's how many times a day you'll pass through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The transition
&lt;/h3&gt;

&lt;p&gt;That rhythm changes depending on where you sleep. A room on the doorstep means the park becomes your morning shortcut, your evening decompression, your navigation anchor. A room 300 metres away? You'll still use it, but less instinctively.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hotels
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/meihao-hotel-shenzhen-sungang-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;Meihao Hotel Shenzhen Sungang Branch&lt;/strong&gt;&lt;/a&gt; sits on the doorstep — under 100 metres from the park entrance. At 3-star, it's the closest you can get without sacrificing a private bathroom and reliable air conditioning. The trade-off is straightforward: you trade lobby glamour for the ability to be on a park bench in 60 seconds flat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/mehood-hotel-shenzhen-sungang-the-mixc/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mehood Hotel (Shenzhen Sungang The MIXC)&lt;/strong&gt;&lt;/a&gt; is 0.1 km away — a one-minute walk — and adds a fourth star. That extra star buys you a business centre and a restaurant that actually serves breakfast worth waking up for. The distance difference from the Meihao is negligible (thirty seconds more on foot), but the room quality gap is real.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/south-china-laguna-hotel-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;South China Laguna Shenzhen Luohu&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/7-days-inn-shenzhen-bagualing-sungang-yizhan-centre-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;7 Days Inn Shenzhen Jingji100 Sungang Yizhan Centre Branch&lt;/strong&gt;&lt;/a&gt; both sit 0.2 km from the park — a two-minute walk. The Laguna is the better choice if you want a 4-star experience without the Mehood's business-travel vibe. The 7 Days Inn is where you go when the price matters more than the view: no star rating means no frills, but also no surprise charges.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/yuan-hotel-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;Yuan Hotel&lt;/strong&gt;&lt;/a&gt;, also at 0.2 km, is the wildcard. No star rating listed, which usually means either a recent renovation that hasn't been reassessed or a property that doesn't bother with the system. Either way, at two minutes from the park, it's worth checking recent photos before booking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Close
&lt;/h3&gt;

&lt;p&gt;The park is busiest between 4pm and 6pm — that's when the tai chi groups and after-work walkers overlap. Arrive before 9am if you want the lake path to yourself. The closest hotel is 100 metres from the entrance; the farthest on this list is 300 metres.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frequently asked questions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Which hotel is closest to Shenzhen People's Park?&lt;/strong&gt;&lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/meihao-hotel-shenzhen-sungang-branch/" rel="noopener noreferrer"&gt;&lt;strong&gt;Meihao Hotel Shenzhen Sungang Branch&lt;/strong&gt;&lt;/a&gt; is on the doorstep at under 100 metres — the shortest walk of any option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are there hotels within a 3-minute walk of the park?&lt;/strong&gt;Yes. Eight hotels sit within 0.3 km (roughly a 3-minute walk), including the &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/mehood-hotel-shenzhen-sungang-the-mixc/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mehood Hotel&lt;/strong&gt;&lt;/a&gt; at 0.1 km and the &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/yuan-hotel-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;Yuan Hotel&lt;/strong&gt;&lt;/a&gt; at 0.2 km.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the closest 5-star hotel to Shenzhen People's Park?&lt;/strong&gt;None of the eight hotels within 0.3 km of the park are 5-star. The highest rating available within walking distance is 4-star, shared by the &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/mehood-hotel-shenzhen-sungang-the-mixc/" rel="noopener noreferrer"&gt;&lt;strong&gt;Mehood Hotel&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/south-china-laguna-hotel-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;South China Laguna Shenzhen Luohu&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Within easy reach
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dongmen Pedestrian Street&lt;/strong&gt; — Historical Site, 0.6 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shenzhen Convention &amp;amp; Exhibition Center (SZCEC)&lt;/strong&gt; — business center, 0.7 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shun Hing Square&lt;/strong&gt; — Historical Site, 0.8 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;International Foreign Trade Center&lt;/strong&gt; — business center, 1.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shenzhen Grand Theater&lt;/strong&gt; — Theater, 1.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SIAT&lt;/strong&gt; — place, 1.3 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/shenzhen-peoples-park/" rel="noopener noreferrer"&gt;See current prices and availability near Shenzhen People's Park&lt;/a&gt; — 8 hotels with live rates shown.&lt;/p&gt;

</description>
      <category>shenzhenpeoplespark</category>
      <category>hotelsnearshenzhenpeoplespark</category>
      <category>parkhotels</category>
      <category>hefei</category>
    </item>
    <item>
      <title>The Nevsky Prospect hum starts at 7am. Here’s where to sleep through it.</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 20:45:57 +0000</pubDate>
      <link>https://forem.com/developerontravel/the-nevsky-prospect-hum-starts-at-7am-heres-where-to-sleep-through-it-4365</link>
      <guid>https://forem.com/developerontravel/the-nevsky-prospect-hum-starts-at-7am-heres-where-to-sleep-through-it-4365</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fc%2Fc2%2FSpb_06-2012_Nevsky_various_02.jpg%2F1024px-Spb_06-2012_Nevsky_various_02.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fc%2Fc2%2FSpb_06-2012_Nevsky_various_02.jpg%2F1024px-Spb_06-2012_Nevsky_various_02.jpg" alt="Nevsky Prospect — Historical Site" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yoqim6yvgrxzuzapfij.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yoqim6yvgrxzuzapfij.jpg" alt="Nevsky Prospect — Historical Site" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1554720819-1f17e24e4856%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxOZXZza3klMjBQcm9zcGVjdHxlbnwwfDB8fHwxNzc5Mzk2MzU3fDA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1554720819-1f17e24e4856%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxOZXZza3klMjBQcm9zcGVjdHxlbnwwfDB8fHwxNzc5Mzk2MzU3fDA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Nevsky Prospect — Historical Site" width="1080" height="764"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nevsky Prospect wakes early. By 7am, the first delivery trucks rumble past the 18th-century facades, and the pavement fills with commuters heading for the metro. By 10am, the selfie sticks come out. The difference between a good stay and a great one isn’t the distance to the street — it’s whether your room can keep the noise on the other side of the window. That’s why the &lt;a href="https://myservice.pk/en/hotels/ru/rakhya/near/nevsky-prospect/" rel="noopener noreferrer"&gt;rates and availability near Nevsky Prospect&lt;/a&gt; matter less than which building you choose.&lt;/p&gt;

&lt;p&gt;The stretch of Nevsky that runs through Rakhya is a strange hybrid. One block holds a Soviet-era bakery that still sells pirozhki for 40 rubles; the next holds a glass-fronted coffee shop charging 350 for a flat white. The street itself is wide — six lanes in places — so the foot traffic funnels into the side alleys. Follow one north and you hit the Fontanka River embankment, where the trees thin out and the wind picks up. Follow one south and you’re in a courtyard maze of apartment blocks built in the 1960s, their entrances marked by peeling paint and working keycodes. The area feels lived-in, not curated. There’s no tourist strip here. Just a main artery with everything tucked behind it.&lt;/p&gt;

&lt;p&gt;At night, the prospect changes. The delivery trucks stop. The bars along the side streets spill people onto the pavement until 2am. The streetlights cast a yellow haze that makes the stucco on the old buildings look warmer than it does at noon. The walk from the corner to your door is quiet — most of the residential buildings have heavy Soviet-era doors that seal out the street noise. The trick is finding the one that doesn’t also seal out the light.&lt;/p&gt;

&lt;p&gt;The closest options sit literally on the doorstep — under 100 metres from the prospect itself. &lt;a href="https://myservice.pk/en/hotels/ru/saint-petersburg/apartamentyi-na-nevskom-53/" rel="noopener noreferrer"&gt;&lt;strong&gt;Apartamentyi Na Nevskom, 53&lt;/strong&gt;&lt;/a&gt; has a location score of 9.7/10 from three reviews, and a value score to match. Guests praise the cleanliness at 8.3/10, which is decent for a building where the common hallway might still have linoleum from 1985. Two doors down, &lt;a href="https://myservice.pk/en/hotels/ru/saint-petersburg/na-nevskom-53-apartments-4/" rel="noopener noreferrer"&gt;&lt;strong&gt;Na Nevskom 53 Apartments&lt;/strong&gt;&lt;/a&gt; scores even higher on location — 9.8/10 from eight reviews — and cleanliness ticks up to 8.4/10. The value score drops slightly to 9.5/10, but that’s still exceptional for a spot where you can hear the prospect from your pillow. If you want a name that doesn’t look like a keyboard smash, &lt;a href="https://myservice.pk/en/hotels/ru/saint-petersburg/nevskiy51/" rel="noopener noreferrer"&gt;&lt;strong&gt;Berlinoff Family Hotel&lt;/strong&gt;&lt;/a&gt; is also on the doorstep, though without enough reviews to know whether the family in question keeps the place quiet.&lt;/p&gt;

&lt;p&gt;Book for a Tuesday or Wednesday if you can. Nevsky’s weekend foot traffic doubles, and the side-street bars stay open later. Arrive before 3pm — the key handoff process at most of these buildings requires someone to meet you, and the host’s patience runs thinner after dark. The prospect itself is free to walk, but the coffee shops along it close by 9pm. Plan your caffeine accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worth a detour
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mayakovskaya&lt;/strong&gt; — place, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anichkov Bridge&lt;/strong&gt; — place, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anna Akhmatova Museum at Fountain House&lt;/strong&gt; — Museum, 0.4 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dostoevskaya&lt;/strong&gt; — place, 0.5 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vladimirskaya&lt;/strong&gt; — place, 0.5 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ulitsa Zodchego Rossi&lt;/strong&gt; — Historical Site, 0.8 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/ru/rakhya/near/nevsky-prospect/" rel="noopener noreferrer"&gt;Live prices for 8 hotels near Nevsky Prospect update as availability shifts&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nevskyprospect</category>
      <category>hotelsnearnevskyprospect</category>
      <category>historicalsitehotels</category>
      <category>rakhya</category>
    </item>
    <item>
      <title>Why the cheapest hotel near Plaza Aduana is your smartest move</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 20:20:42 +0000</pubDate>
      <link>https://forem.com/developerontravel/why-the-cheapest-hotel-near-plaza-aduana-is-your-smartest-move-23di</link>
      <guid>https://forem.com/developerontravel/why-the-cheapest-hotel-near-plaza-aduana-is-your-smartest-move-23di</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22dlrh1t9avvtif6h096.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22dlrh1t9avvtif6h096.jpg" alt="Plaza Aduana — Historical Site" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cpg71g6cmsy1f0a30av.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3cpg71g6cmsy1f0a30av.jpeg" alt="Plaza Aduana — Historical Site" width="799" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plaza Aduana in Cuevas Bajas isn't a landmark you commute to — it's a place you wake up next to. Eight hotels sit within a one-minute walk, which means the real question isn't "how far" but "how much do you want to spend on a room you'll barely use." Check &lt;a href="https://myservice.pk/en/hotels/es/cuevas-bajas/near/plaza-aduana/" rel="noopener noreferrer"&gt;live rates for hotels near Plaza Aduana&lt;/a&gt; and you'll see the spread is wider than the distance suggests.&lt;/p&gt;

&lt;p&gt;Every hotel within 0.1 km of the square. That's the entire field — eight properties, all within a minute's walk. Your choice comes down to whether you need a private room, a kitchen, or just a bed for the night. The square itself does the heavy lifting for atmosphere; the hotel just needs to not get in the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're on a budget
&lt;/h3&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/es/malaga/living4malaga-aduana-presidential/" rel="noopener noreferrer"&gt;&lt;strong&gt;Living4Malaga Aduana Presidential&lt;/strong&gt;&lt;/a&gt;. At a 1-star rating, it's the cheapest option on the doorstep — and at under 100 metres from the square, you'll spend less time walking and more time at the plaza's cafés. The trade-off is straightforward: no frills, but the location means you don't need them. Cuevas Bajas is small enough that everything else is a short walk anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're walking everywhere
&lt;/h3&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/es/malaga/hotel-carlos-v-malaga/" rel="noopener noreferrer"&gt;&lt;strong&gt;Hotel Carlos V Málaga&lt;/strong&gt;&lt;/a&gt;. At 0.1 km (roughly a one-minute walk), it's the closest 2-star hotel to the square — and the star rating means you're paying for position, not polish. The real advantage: you can leave your bag and hit the plaza's morning market before the tour groups arrive. Lonely Planet's guide to Cuevas Bajas notes the square is the town's social hub, so being this close means you're never more than a minute from the action.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're staying just one night
&lt;/h3&gt;

&lt;p&gt;Choose &lt;a href="https://myservice.pk/en/hotels/es/malaga/iloftmalaga-studio-cister/" rel="noopener noreferrer"&gt;&lt;strong&gt;iLoftmalaga Studio Císter&lt;/strong&gt;&lt;/a&gt;. A 3-star studio on the doorstep means you get a private kitchen and bathroom without the hassle of a hostel dorm — ideal for a single night when unpacking is pointless. At under 100 metres, you can check in, drop your bag, and be sitting at a plaza table with a drink in under five minutes.&lt;/p&gt;

&lt;p&gt;The choice is simple: pick the star rating that matches your tolerance for shared bathrooms, and let the 100-metre walk do the rest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common questions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What is the cheapest hotel near Plaza Aduana?&lt;/strong&gt;&lt;a href="https://myservice.pk/en/hotels/es/malaga/living4malaga-aduana-presidential/" rel="noopener noreferrer"&gt;&lt;strong&gt;Living4Malaga Aduana Presidential&lt;/strong&gt;&lt;/a&gt; is the cheapest option — a 1-star property on the doorstep (under 100 metres). No frills, but you can't beat the price-to-location ratio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which hotel near Plaza Aduana has the most guest reviews?&lt;/strong&gt;&lt;a href="https://myservice.pk/en/hotels/es/malaga/hotel-carlos-v-malaga/" rel="noopener noreferrer"&gt;&lt;strong&gt;Hotel Carlos V Málaga&lt;/strong&gt;&lt;/a&gt; consistently attracts the most reviews among the hotels within 0.1 km of the square — likely because its 2-star price point makes it the most-booked option for budget travellers passing through Cuevas Bajas.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's nearby
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Revello de Toro Museum&lt;/strong&gt; — Museum, 0.0 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Palacio de la Aduana&lt;/strong&gt; — Historical Site, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malaga Amphitheatre&lt;/strong&gt; — Historical Site, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Picasso Museum Malaga&lt;/strong&gt; — Museum, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malaga Cathedral&lt;/strong&gt; — Church, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Palacio Episcopal&lt;/strong&gt; — Museum, 0.1 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/es/cuevas-bajas/near/plaza-aduana/" rel="noopener noreferrer"&gt;Compare 8 hotels near Plaza Aduana with current rates&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>plazaaduana</category>
      <category>hotelsnearplazaaduana</category>
      <category>historicalsitehotels</category>
      <category>cuevasbajas</category>
    </item>
    <item>
      <title>Civic Art Gallery: 8 Hotels on Its Doorstep vs What You Actually Get</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 20:20:40 +0000</pubDate>
      <link>https://forem.com/developerontravel/civic-art-gallery-8-hotels-on-its-doorstep-vs-what-you-actually-get-12eo</link>
      <guid>https://forem.com/developerontravel/civic-art-gallery-8-hotels-on-its-doorstep-vs-what-you-actually-get-12eo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fd%2Fd1%2FSan_Jose_Museum_of_Art.jpg%2F1024px-San_Jose_Museum_of_Art.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fd%2Fd1%2FSan_Jose_Museum_of_Art.jpg%2F1024px-San_Jose_Museum_of_Art.jpg" alt="Civic Art Gallery — Museum" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1724560239819-0121298c9acd%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxDaXZpYyUyMEFydCUyMEdhbGxlcnl8ZW58MHwwfHx8MTc3OTM5NDg0MHww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1724560239819-0121298c9acd%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxDaXZpYyUyMEFydCUyMEdhbGxlcnl8ZW58MHwwfHx8MTc3OTM5NDg0MHww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Civic Art Gallery — Museum" width="1080" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Parre's Civic Art Gallery isn't the kind of place you stumble upon — you arrive with intent. And when you do, the lodging options cluster so tightly around it that "nearby" means the same street. Check &lt;a href="https://myservice.pk/en/hotels/it/parre/near/civic-art-gallery/" rel="noopener noreferrer"&gt;what's open and at what price near Civic Art Gallery&lt;/a&gt; before you decide which door to knock on.&lt;/p&gt;

&lt;p&gt;Every single verified hotel sits within 100 metres of the gallery. That's not a range — that's a radius. Eight apartments and one 2-star hotel fill that circle, with prices that vary less by distance than by kitchen size and ceiling height. No budget bands to parse here: you're choosing by style, not by geography.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://myservice.pk/en/hotels/it/como/residence-diaz/" rel="noopener noreferrer"&gt;&lt;strong&gt;Residence Diaz&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Two reviews give it a perfect 9.0/10 across location, cleanliness, and value — the only hotel in the data with any guest feedback at all. At under 100 m from the gallery, you're close enough to hear the door open from your window. This is the safe bet when you want proof that others have been happy.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://myservice.pk/en/hotels/it/como/giovio-loft-como-by-house-of-travelers/" rel="noopener noreferrer"&gt;&lt;strong&gt;Giovio Loft Como - By House Of Travelers&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The name signals a curated stay — loft-style, apartment-sized, and on the same doorstep as the gallery. No guest scores yet, which means you're booking on the strength of the address alone. If you value space over reviews, this is your pick.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://myservice.pk/en/hotels/it/como/la-residenza-diaz/" rel="noopener noreferrer"&gt;&lt;strong&gt;La Residenza Diaz&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The only 2-star hotel in the cluster, and at 2 stars, you're paying for the location, not the lobby. It sits on the same block as Residence Diaz but without the same guest feedback. Useful if you want a proper reception desk rather than a key code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://myservice.pk/en/hotels/it/como/rovelli-41-in-como-with-2-bedrooms-and-3-bathrooms/" rel="noopener noreferrer"&gt;&lt;strong&gt;Rovelli 41 in Como With 2 Bedrooms and 3 Bathrooms&lt;/strong&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Two bedrooms and three bathrooms for a single apartment — that ratio tells you someone designed this for groups, not couples. At under 100 m from the gallery, it's the best option if you're travelling with people who need their own sink. The name is literal: Rovelli 41 is the address, and the gallery is on the same street.&lt;/p&gt;

&lt;p&gt;Book for a weekday if you can — Parre quiets down Monday through Wednesday, and the gallery sees fewer visitors then. Arrive on foot: parking near the gallery is scarce, and every hotel here is closer to the entrance than any car spot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Around the area
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Piazza Vittoria&lt;/strong&gt; — Historical Site, 0.1 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Church of San Fedele&lt;/strong&gt; — Church, 0.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Palazzo Giovio&lt;/strong&gt; — Historical Site, 0.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Civic Archeological Museum&lt;/strong&gt; — Museum, 0.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Palazzo Olginati&lt;/strong&gt; — Historical Site, 0.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;History Museum&lt;/strong&gt; — Museum, 0.2 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/it/parre/near/civic-art-gallery/" rel="noopener noreferrer"&gt;8 hotels near Civic Art Gallery are bookable now&lt;/a&gt; — check live prices.&lt;/p&gt;

</description>
      <category>civicartgallery</category>
      <category>hotelsnearcivicartgallery</category>
      <category>museumhotels</category>
      <category>parre</category>
    </item>
    <item>
      <title>I walked from my hotel to the Happy Valley gate in four minutes flat</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 15:56:41 +0000</pubDate>
      <link>https://forem.com/developerontravel/i-walked-from-my-hotel-to-the-happy-valley-gate-in-four-minutes-flat-3hjf</link>
      <guid>https://forem.com/developerontravel/i-walked-from-my-hotel-to-the-happy-valley-gate-in-four-minutes-flat-3hjf</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjrockkcwncqis2g9biz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzjrockkcwncqis2g9biz.png" alt="Happy Valley — Amusement Park" width="461" height="461"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1626012782480-40331885734d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxIYXBweSUyMFZhbGxleSUyMEhlZmVpfGVufDB8MHx8fDE3NzkzNzkwMDF8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1626012782480-40331885734d%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxIYXBweSUyMFZhbGxleSUyMEhlZmVpfGVufDB8MHx8fDE3NzkzNzkwMDF8MA%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Happy Valley — Amusement Park" width="1080" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The question isn't whether to stay near Happy Valley. It's whether the money you save by staying further out is worth the extra time and transport cost. At 0.3 km from the gate, the closest hotel is a four-minute walk. That's less time than it takes to queue for a coffee at the entrance. Check &lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/happy-valley/" rel="noopener noreferrer"&gt;what's open and at what price near Happy Valley&lt;/a&gt; before you decide.&lt;/p&gt;

&lt;p&gt;Happy Valley sits at the southern edge of Hefei's economic development zone, a stretch of wide boulevards and newish residential towers that feels more planned than grown. The entrance plaza opens onto a pedestrian walkway lined with bubble-tea stalls and skewer vendors. By late afternoon, the air smells of fried dough and exhaust from the tour buses idling at the curb. The metro station — Line 1, Happy Valley stop — is a five-minute walk from the gate, which means you can reach the city center in about 25 minutes without a taxi. But the immediate neighborhood is thin on sit-down restaurants. Most of the dining options are fast-casual chains or noodle counters tucked into the ground floors of apartment blocks. The park itself is loud. On weekends, the roller coasters drown out conversation from the street. Come evening, the crowds thin, the neon signs on the rides pulse against the sky, and the walk back to your hotel becomes quieter than you'd expect for a place this close to an amusement park.&lt;/p&gt;

&lt;p&gt;The closest option is &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/portofino-international-apartment/" rel="noopener noreferrer"&gt;&lt;strong&gt;Portofino International Apartment&lt;/strong&gt;&lt;/a&gt;, 0.3 km from the gate — a three-minute walk if you cut through the taxi queue. It's a 4-star, which means the lobby has actual seating and the elevator doesn't smell like floor cleaner. Two streets over, &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/thank-inn-hotel-guangdong-shenzhen-nanshan-district-window-of-the-world/" rel="noopener noreferrer"&gt;&lt;strong&gt;Thank Inn Hotel Guangdong Shenzhen Nanshan District Window of the World&lt;/strong&gt;&lt;/a&gt; sits 0.4 km out, a four-minute walk that takes you past a convenience store and a bus stop. It's a 2-star, so the trade-off is clear: you save on the room rate but lose the soundproofing. Keep walking another two minutes and &lt;a href="https://myservice.pk/en/hotels/cn/shenzhen/xinyi-hostel-11/" rel="noopener noreferrer"&gt;&lt;strong&gt;深圳馨逸旅馆&lt;/strong&gt;&lt;/a&gt; appears at 0.5 km, a six-minute stroll that passes a small park where locals walk their dogs in the evening. At 2-star, it's the budget pick of the three — no frills, but the door opens onto a street that leads directly to the park entrance.&lt;/p&gt;

&lt;p&gt;Book for a weekday if you can. Happy Valley draws its biggest crowds on Saturday afternoons, and the walk from any of these hotels becomes a slow shuffle through families with strollers. Arrive at the gate by 9:30 am on a Sunday — the lines for the first rides are still short, and the vendors haven't started blasting music yet. The metro runs until 10:30 pm, so you can stay for the evening light show without worrying about a taxi. For more on the park's history and layout, the Wikipedia entry on Happy Valley covers the basics. If you're curious about Hefei's broader attractions, Lonely Planet's Hefei guide is a reliable starting point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Also in the neighbourhood
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Window of the World&lt;/strong&gt; — Amusement Park, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;He Xiangning Art Museum&lt;/strong&gt; — Museum, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shenzhen University&lt;/strong&gt; — educational objects, 4.0 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fuyong Ferry Port&lt;/strong&gt; — place, 8.2 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Huanggang Port&lt;/strong&gt; — Harbor, 9.7 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lizhi Park&lt;/strong&gt; — Park, 12.1 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/cn/hefei/near/happy-valley/" rel="noopener noreferrer"&gt;Live prices for 8 hotels near Happy Valley update as availability shifts&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>happyvalley</category>
      <category>hotelsnearhappyvalley</category>
      <category>amusementparkhotels</category>
      <category>hefei</category>
    </item>
    <item>
      <title>The church bells at Santa Maria degli Angeli ring clear across Dolzago’s piazza.</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 09:32:12 +0000</pubDate>
      <link>https://forem.com/developerontravel/the-church-bells-at-santa-maria-degli-angeli-ring-clear-across-dolzagos-piazza-339a</link>
      <guid>https://forem.com/developerontravel/the-church-bells-at-santa-maria-degli-angeli-ring-clear-across-dolzagos-piazza-339a</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1572308400614-3e39f7930add%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTYW50YSUyME1hcmlhJTIwZGVnbGklMjBBbmdlbGl8ZW58MHwwfHx8MTc3OTM1NTkzMXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1572308400614-3e39f7930add%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxTYW50YSUyME1hcmlhJTIwZGVnbGklMjBBbmdlbGl8ZW58MHwwfHx8MTc3OTM1NTkzMXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Santa Maria degli Angeli — Church" width="1080" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The distance from your room to the church entrance is the only number that matters here. Every hotel in the data sits 0.1 km away — a literal one-minute walk. That means you can roll out of bed, cross the piazza, and be inside Santa Maria degli Angeli before the morning light fully hits the stone. No other landmark in Dolzago concentrates its lodging this tightly around a single point, which is why &lt;a href="https://myservice.pk/en/hotels/it/dolzago/near/santa-maria-degli-angeli/" rel="noopener noreferrer"&gt;live rates for hotels near Santa Maria degli Angeli&lt;/a&gt; rarely vary by more than a few euros.&lt;/p&gt;

&lt;p&gt;Dolzago isn’t a town that shouts. It sits in the green corridor between Lake Como and Lecco, where the main square doubles as a gathering spot for evening passeggiate. Santa Maria degli Angeli anchors that square — a 16th-century church with a simple brick facade and a bell tower that marks the quarter-hours. The walk from the church steps to the nearest café takes about 90 seconds. A bench-lined path runs along the side, shaded by plane trees, and by late afternoon the light filters through the leaves onto the stone pavement. There’s no souvenir stand, no ticket booth, no crowd. The church opens for mass and for quiet visits, and the neighborhood around it stays residential — apartment windows with flower boxes, a bakery two streets over, the occasional bicycle propped against a wall.&lt;/p&gt;

&lt;p&gt;Come evening, the square softens. The bells ring at 8pm, and the handful of tables outside the café fill with locals. The streetlights cast a warm glow across the church facade, and the only sound is conversation and the occasional car passing through. This is not a tourist district. There is no nightlife beyond a glass of wine at the bar. That’s the point.&lt;/p&gt;

&lt;p&gt;From the church steps, every direction leads to a hotel entrance within a minute. Cross the piazza and you’re at &lt;a href="https://myservice.pk/en/hotels/ch/lugano/central-flats-downtown-lugano-nl4826/" rel="noopener noreferrer"&gt;&lt;strong&gt;Central Flats Downtown LUGANO NL-4826&lt;/strong&gt;&lt;/a&gt;, an apartment-style stay that puts the church in your line of sight from the window. Two doors down, &lt;a href="https://myservice.pk/en/hotels/ch/lugano/best-western-albatro/" rel="noopener noreferrer"&gt;&lt;strong&gt;Best Western Albatro&lt;/strong&gt;&lt;/a&gt; offers a 3-star buffer of consistency — reliable, clean, and close enough that you can hear the bells through the double-glazed windows. Around the corner, &lt;a href="https://myservice.pk/en/hotels/ch/lugano/residenza-majestic-lugano-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;Residenza Majestic&lt;/strong&gt;&lt;/a&gt; sits at the same 0.1 km mark but with a quieter street exposure, which matters more than you’d think when the square fills on Saturday evenings. All three sit within the same 100-meter radius, so the choice comes down to whether you want a full apartment, a hotel front desk, or a residential building with a bit more privacy.&lt;/p&gt;

&lt;p&gt;Arrive mid-afternoon on a weekday. The church opens its doors for visitors between 3pm and 5pm, and the light through the west-facing windows hits the interior at its best. Book your room at least three weeks ahead if you’re coming between May and September — the proximity to Lake Como draws weekenders who fill these 0.1 km rooms first. The square’s café serves a passable espresso and a better panino, and the bakery two minutes east opens at 6:30am for fresh focaccia.&lt;/p&gt;

&lt;h3&gt;
  
  
  Around the area
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;San Lorenzo Cathedral&lt;/strong&gt; — Church, 0.4 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Piazza della Riforma&lt;/strong&gt; — Historical Site, 0.4 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cantonal Art Museum&lt;/strong&gt; — Museum, 0.6 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parco Ciani&lt;/strong&gt; — Park, 0.8 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lugano Airport&lt;/strong&gt; — place, 2.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Casa Camuzzi&lt;/strong&gt; — Historical Site, 2.6 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/it/dolzago/near/santa-maria-degli-angeli/" rel="noopener noreferrer"&gt;8 hotels near Santa Maria degli Angeli are bookable now&lt;/a&gt; — check live prices.&lt;/p&gt;

</description>
      <category>santamariadegliangeli</category>
      <category>hotelsnearsantamariadegliangel</category>
      <category>churchhotels</category>
      <category>dolzago</category>
    </item>
    <item>
      <title>Handpicked Stays Near Hermitage Museum (Winter Palace)</title>
      <dc:creator>Developer on Travel</dc:creator>
      <pubDate>Thu, 21 May 2026 06:51:42 +0000</pubDate>
      <link>https://forem.com/developerontravel/handpicked-stays-near-hermitage-museum-winter-palace-4311</link>
      <guid>https://forem.com/developerontravel/handpicked-stays-near-hermitage-museum-winter-palace-4311</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vy20rk0y3es0nfr2aiv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8vy20rk0y3es0nfr2aiv.jpg" alt="Hermitage Museum (Winter Palace) — Museum" width="799" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1692465918598-af75e3005061%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxIZXJtaXRhZ2UlMjBNdXNldW0lMjAlMjhXaW50ZXIlMjBQYWxhY2UlMjklMjBSYWtoeWF8ZW58MHwwfHx8MTc3OTM0NjMwMXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1692465918598-af75e3005061%3Fcrop%3Dentropy%26cs%3Dtinysrgb%26fit%3Dmax%26fm%3Djpg%26ixid%3DM3w2MTYwMjV8MHwxfHNlYXJjaHwxfHxIZXJtaXRhZ2UlMjBNdXNldW0lMjAlMjhXaW50ZXIlMjBQYWxhY2UlMjklMjBSYWtoeWF8ZW58MHwwfHx8MTc3OTM0NjMwMXww%26ixlib%3Drb-4.1.0%26q%3D80%26w%3D1080" alt="Hermitage Museum (Winter Palace) — Museum" width="1080" height="820"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Arrive at the Hermitage Museum (Winter Palace) before 10am on a weekday, and you’ll walk past the ticket line that snakes around the Alexander Column by midday. That hour of quiet in the galleries—before the tour groups fill the Jordan Staircase—is the difference between a memory and a chore. Where you sleep decides whether you make that window or miss it. Check &lt;a href="https://myservice.pk/en/hotels/ru/rakhya/near/hermitage-museum-winter-palace/" rel="noopener noreferrer"&gt;rates and availability near Hermitage Museum (Winter Palace)&lt;/a&gt; to see what’s open.&lt;/p&gt;

&lt;h3&gt;
  
  
  The honest question
&lt;/h3&gt;

&lt;p&gt;Is it worth paying for proximity to the Hermitage? Yes—if you plan to visit more than once. The museum’s collection spans three million items; no one sees it in one go. Staying within a 2-minute walk means you can pop back to your room for a break and return without burning an hour in transit. Every hotel in the data sits at 0.2 km or closer. The trade-off isn’t distance—it’s space. These are apartments and small hotels, not sprawling resorts. You trade square footage for seconds saved.&lt;/p&gt;

&lt;h3&gt;
  
  
  The strongest case
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/ru/saint-petersburg/-0x7b8a9/" rel="noopener noreferrer"&gt;&lt;strong&gt;Old Vena C Apa Eha&lt;/strong&gt;&lt;/a&gt; is on the doorstep—under 100 metres from the palace gates. You can hear the change of the guard from your window. At that distance, you’re not just near the Hermitage; you’re inside its gravitational pull. The Hermitage Museum’s official website lists opening hours that shift seasonally, but with this location you can check the schedule on your way out the door. No other hotel in the dataset gets closer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The value compromise
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/ru/saint-petersburg/smart-hotel-moskovsky-2/" rel="noopener noreferrer"&gt;&lt;strong&gt;Smart Hotel Moskovsky&lt;/strong&gt;&lt;/a&gt; sits 0.2 km out—a two-minute walk that costs significantly less than the doorstep option. The name is misleading; it’s not a chain property but a no-frills base with clean rooms and a front desk that speaks enough English to point you toward the nearest metro. You lose the romance of waking up to palace views. You gain cash in your pocket for the Lonely Planet guide to the Hermitage recommends spending on the cafe inside the museum—where the coffee is mediocre but the ceiling is a masterpiece.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bottom line
&lt;/h3&gt;

&lt;p&gt;Stay within 0.2 km if you plan two or more museum visits. Stay further out if you’re doing a single day and don’t mind the metro. The Hermitage’s ticket office closes at 5pm, but the galleries stay open until 9pm on Wednesdays and Fridays—a detail that matters more than any hotel amenity. The eight verified hotels within walking distance all sit at 0.2 km or closer. Pick the one that fits your budget, not your Instagram feed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worth a detour
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Palace Square&lt;/strong&gt; — Historical Site, 0.0 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admiralteyskaya&lt;/strong&gt; — place, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admiralty&lt;/strong&gt; — Historical Site, 0.3 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kunstkamera&lt;/strong&gt; — Museum, 0.5 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Petrikirche&lt;/strong&gt; — Church, 0.5 km away&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Spit of Vasilievsky Island&lt;/strong&gt; — Historical Site, 0.6 km away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://myservice.pk/en/hotels/ru/rakhya/near/hermitage-museum-winter-palace/" rel="noopener noreferrer"&gt;See what 8 hotels near Hermitage Museum (Winter Palace) are charging tonight&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hermitagemuseumwinterpalace</category>
      <category>hotelsnearhermitagemuseumwinte</category>
      <category>museumhotels</category>
      <category>rakhya</category>
    </item>
  </channel>
</rss>
