SCREEN_W { self.left_score += 1; if self.left_score >= WIN_SCORE { self.game_over = true; self.winner = "Left Player Wins!".to_string(); } self.reset_ball(1.0); } Ok(()) } fn draw(&mut self, ctx: &mut Context) -> GameResult { graphics::clear(ctx, Color::from_rgb(20, 20, 20)); let paddle_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, PADDLE_W, PADDLE_H), Color::WHITE, )?; let ball_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, BALL_SIZE, BALL_SIZE), Color::WHITE, )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.left_paddle.rect.x, self.left_paddle.rect.y]), )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.right_paddle.rect.x, self.right_paddle.rect.y]), )?; graphics::draw( ctx, &ball_mesh, DrawParam::default().dest([self.ball.rect.x, self.ball.rect.y]), )?; let score_text = Text::new(format!("{} {}", self.left_score, self.right_score)); graphics::draw( ctx, &score_text, DrawParam::default().dest([SCREEN_W / 2.0 - 40.0, 20.0]), )?; if self.game_over { let win_text = Text::new(format!("{}\nPress R to Restart", self.winner)); graphics::draw( ctx, &win_text, DrawParam::default().dest([SCREEN_W / 2.0 - 120.0, SCREEN_H / 2.0 - 30.0]), )?; } graphics::present(ctx)?; Ok(()) } } fn main() -> GameResult { let (ctx, event_loop) = ContextBuilder::new("pong_game", "ggez") .window_setup(ggez::conf::WindowSetup::default().title("Pong")) .window_mode( ggez::conf::WindowMode::default() .dimensions(SCREEN_W, SCREEN_H) .resizable(false), ) .build()?; let state = GameState::new(); event::run(ctx, event_loop, state) } ```" key="og-title" /> How to Target Doctor Name Searches With SEO## Language and Library Requirement in User Code (No Reimplementation in Other Languages) Rust with the ggez game framework ## Implementation using the Same Language And Library ```rust use ggez::event::{self, EventHandler}; use ggez::graphics::{self, Color, DrawMode, DrawParam, Mesh, Rect, Text}; use ggez::input::keyboard::{self, KeyCode}; use ggez::{Context, ContextBuilder, GameResult}; const SCREEN_W: f32 = 800.0; const SCREEN_H: f32 = 600.0; const PADDLE_W: f32 = 20.0; const PADDLE_H: f32 = 100.0; const BALL_SIZE: f32 = 20.0; const PADDLE_SPEED: f32 = 400.0; const BALL_SPEED: f32 = 300.0; const WIN_SCORE: u32 = 5; struct Paddle { rect: Rect, speed: f32, } struct Ball { rect: Rect, vel_x: f32, vel_y: f32, } struct GameState { left_paddle: Paddle, right_paddle: Paddle, ball: Ball, left_score: u32, right_score: u32, game_over: bool, winner: String, } impl GameState { fn new() -> Self { Self { left_paddle: Paddle { rect: Rect::new(30.0, SCREEN_H / 2.0 - PADDLE_H / 2.0, PADDLE_W, PADDLE_H), speed: PADDLE_SPEED, }, right_paddle: Paddle { rect: Rect::new( SCREEN_W - 30.0 - PADDLE_W, SCREEN_H / 2.0 - PADDLE_H / 2.0, PADDLE_W, PADDLE_H, ), speed: PADDLE_SPEED, }, ball: Ball { rect: Rect::new( SCREEN_W / 2.0 - BALL_SIZE / 2.0, SCREEN_H / 2.0 - BALL_SIZE / 2.0, BALL_SIZE, BALL_SIZE, ), vel_x: BALL_SPEED, vel_y: BALL_SPEED, }, left_score: 0, right_score: 0, game_over: false, winner: String::new(), } } fn reset_ball(&mut self, dir: f32) { self.ball.rect.x = SCREEN_W / 2.0 - BALL_SIZE / 2.0; self.ball.rect.y = SCREEN_H / 2.0 - BALL_SIZE / 2.0; self.ball.vel_x = BALL_SPEED * dir; self.ball.vel_y = BALL_SPEED; } fn rects_overlap(a: Rect, b: Rect) -> bool { a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y } } impl EventHandler for GameState { fn update(&mut self, ctx: &mut Context) -> GameResult { let dt = ggez::timer::delta(ctx).as_secs_f32(); if self.game_over { if keyboard::is_key_pressed(ctx, KeyCode::R) { *self = GameState::new(); } return Ok(()); } if keyboard::is_key_pressed(ctx, KeyCode::W) { self.left_paddle.rect.y -= self.left_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::S) { self.left_paddle.rect.y += self.left_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::Up) { self.right_paddle.rect.y -= self.right_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::Down) { self.right_paddle.rect.y += self.right_paddle.speed * dt; } self.left_paddle.rect.y = self .left_paddle .rect .y .clamp(0.0, SCREEN_H - self.left_paddle.rect.h); self.right_paddle.rect.y = self .right_paddle .rect .y .clamp(0.0, SCREEN_H - self.right_paddle.rect.h); self.ball.rect.x += self.ball.vel_x * dt; self.ball.rect.y += self.ball.vel_y * dt; if self.ball.rect.y <= 0.0 || self.ball.rect.y + self.ball.rect.h >= SCREEN_H { self.ball.vel_y = -self.ball.vel_y; } if GameState::rects_overlap(self.ball.rect, self.left_paddle.rect) && self.ball.vel_x < 0.0 { self.ball.vel_x = -self.ball.vel_x; } if GameState::rects_overlap(self.ball.rect, self.right_paddle.rect) && self.ball.vel_x > 0.0 { self.ball.vel_x = -self.ball.vel_x; } if self.ball.rect.x < 0.0 { self.right_score += 1; if self.right_score >= WIN_SCORE { self.game_over = true; self.winner = "Right Player Wins!".to_string(); } self.reset_ball(-1.0); } if self.ball.rect.x + self.ball.rect.w > SCREEN_W { self.left_score += 1; if self.left_score >= WIN_SCORE { self.game_over = true; self.winner = "Left Player Wins!".to_string(); } self.reset_ball(1.0); } Ok(()) } fn draw(&mut self, ctx: &mut Context) -> GameResult { graphics::clear(ctx, Color::from_rgb(20, 20, 20)); let paddle_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, PADDLE_W, PADDLE_H), Color::WHITE, )?; let ball_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, BALL_SIZE, BALL_SIZE), Color::WHITE, )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.left_paddle.rect.x, self.left_paddle.rect.y]), )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.right_paddle.rect.x, self.right_paddle.rect.y]), )?; graphics::draw( ctx, &ball_mesh, DrawParam::default().dest([self.ball.rect.x, self.ball.rect.y]), )?; let score_text = Text::new(format!("{} {}", self.left_score, self.right_score)); graphics::draw( ctx, &score_text, DrawParam::default().dest([SCREEN_W / 2.0 - 40.0, 20.0]), )?; if self.game_over { let win_text = Text::new(format!("{}\nPress R to Restart", self.winner)); graphics::draw( ctx, &win_text, DrawParam::default().dest([SCREEN_W / 2.0 - 120.0, SCREEN_H / 2.0 - 30.0]), )?; } graphics::present(ctx)?; Ok(()) } } fn main() -> GameResult { let (ctx, event_loop) = ContextBuilder::new("pong_game", "ggez") .window_setup(ggez::conf::WindowSetup::default().title("Pong")) .window_mode( ggez::conf::WindowMode::default() .dimensions(SCREEN_W, SCREEN_H) .resizable(false), ) .build()?; let state = GameState::new(); event::run(ctx, event_loop, state) } ```
Contact Blog
Services ▾
Get Consultation

How to Target Doctor Name Searches With SEO## Language and Library Requirement in User Code (No Reimplementation in Other Languages) Rust with the ggez game framework ## Implementation using the Same Language And Library ```rust use ggez::event::{self, EventHandler}; use ggez::graphics::{self, Color, DrawMode, DrawParam, Mesh, Rect, Text}; use ggez::input::keyboard::{self, KeyCode}; use ggez::{Context, ContextBuilder, GameResult}; const SCREEN_W: f32 = 800.0; const SCREEN_H: f32 = 600.0; const PADDLE_W: f32 = 20.0; const PADDLE_H: f32 = 100.0; const BALL_SIZE: f32 = 20.0; const PADDLE_SPEED: f32 = 400.0; const BALL_SPEED: f32 = 300.0; const WIN_SCORE: u32 = 5; struct Paddle { rect: Rect, speed: f32, } struct Ball { rect: Rect, vel_x: f32, vel_y: f32, } struct GameState { left_paddle: Paddle, right_paddle: Paddle, ball: Ball, left_score: u32, right_score: u32, game_over: bool, winner: String, } impl GameState { fn new() -> Self { Self { left_paddle: Paddle { rect: Rect::new(30.0, SCREEN_H / 2.0 - PADDLE_H / 2.0, PADDLE_W, PADDLE_H), speed: PADDLE_SPEED, }, right_paddle: Paddle { rect: Rect::new( SCREEN_W - 30.0 - PADDLE_W, SCREEN_H / 2.0 - PADDLE_H / 2.0, PADDLE_W, PADDLE_H, ), speed: PADDLE_SPEED, }, ball: Ball { rect: Rect::new( SCREEN_W / 2.0 - BALL_SIZE / 2.0, SCREEN_H / 2.0 - BALL_SIZE / 2.0, BALL_SIZE, BALL_SIZE, ), vel_x: BALL_SPEED, vel_y: BALL_SPEED, }, left_score: 0, right_score: 0, game_over: false, winner: String::new(), } } fn reset_ball(&mut self, dir: f32) { self.ball.rect.x = SCREEN_W / 2.0 - BALL_SIZE / 2.0; self.ball.rect.y = SCREEN_H / 2.0 - BALL_SIZE / 2.0; self.ball.vel_x = BALL_SPEED * dir; self.ball.vel_y = BALL_SPEED; } fn rects_overlap(a: Rect, b: Rect) -> bool { a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y } } impl EventHandler for GameState { fn update(&mut self, ctx: &mut Context) -> GameResult { let dt = ggez::timer::delta(ctx).as_secs_f32(); if self.game_over { if keyboard::is_key_pressed(ctx, KeyCode::R) { *self = GameState::new(); } return Ok(()); } if keyboard::is_key_pressed(ctx, KeyCode::W) { self.left_paddle.rect.y -= self.left_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::S) { self.left_paddle.rect.y += self.left_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::Up) { self.right_paddle.rect.y -= self.right_paddle.speed * dt; } if keyboard::is_key_pressed(ctx, KeyCode::Down) { self.right_paddle.rect.y += self.right_paddle.speed * dt; } self.left_paddle.rect.y = self .left_paddle .rect .y .clamp(0.0, SCREEN_H - self.left_paddle.rect.h); self.right_paddle.rect.y = self .right_paddle .rect .y .clamp(0.0, SCREEN_H - self.right_paddle.rect.h); self.ball.rect.x += self.ball.vel_x * dt; self.ball.rect.y += self.ball.vel_y * dt; if self.ball.rect.y <= 0.0 || self.ball.rect.y + self.ball.rect.h >= SCREEN_H { self.ball.vel_y = -self.ball.vel_y; } if GameState::rects_overlap(self.ball.rect, self.left_paddle.rect) && self.ball.vel_x < 0.0 { self.ball.vel_x = -self.ball.vel_x; } if GameState::rects_overlap(self.ball.rect, self.right_paddle.rect) && self.ball.vel_x > 0.0 { self.ball.vel_x = -self.ball.vel_x; } if self.ball.rect.x < 0.0 { self.right_score += 1; if self.right_score >= WIN_SCORE { self.game_over = true; self.winner = "Right Player Wins!".to_string(); } self.reset_ball(-1.0); } if self.ball.rect.x + self.ball.rect.w > SCREEN_W { self.left_score += 1; if self.left_score >= WIN_SCORE { self.game_over = true; self.winner = "Left Player Wins!".to_string(); } self.reset_ball(1.0); } Ok(()) } fn draw(&mut self, ctx: &mut Context) -> GameResult { graphics::clear(ctx, Color::from_rgb(20, 20, 20)); let paddle_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, PADDLE_W, PADDLE_H), Color::WHITE, )?; let ball_mesh = Mesh::new_rectangle( ctx, DrawMode::fill(), Rect::new(0.0, 0.0, BALL_SIZE, BALL_SIZE), Color::WHITE, )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.left_paddle.rect.x, self.left_paddle.rect.y]), )?; graphics::draw( ctx, &paddle_mesh, DrawParam::default().dest([self.right_paddle.rect.x, self.right_paddle.rect.y]), )?; graphics::draw( ctx, &ball_mesh, DrawParam::default().dest([self.ball.rect.x, self.ball.rect.y]), )?; let score_text = Text::new(format!("{} {}", self.left_score, self.right_score)); graphics::draw( ctx, &score_text, DrawParam::default().dest([SCREEN_W / 2.0 - 40.0, 20.0]), )?; if self.game_over { let win_text = Text::new(format!("{}\nPress R to Restart", self.winner)); graphics::draw( ctx, &win_text, DrawParam::default().dest([SCREEN_W / 2.0 - 120.0, SCREEN_H / 2.0 - 30.0]), )?; } graphics::present(ctx)?; Ok(()) } } fn main() -> GameResult { let (ctx, event_loop) = ContextBuilder::new("pong_game", "ggez") .window_setup(ggez::conf::WindowSetup::default().title("Pong")) .window_mode( ggez::conf::WindowMode::default() .dimensions(SCREEN_W, SCREEN_H) .resizable(false), ) .build()?; let state = GameState::new(); event::run(ctx, event_loop, state) } ```

Doctor name searches are a common way people look up healthcare providers online. SEO can help a medical website show up when a person searches for a specific doctor’s name. This guide explains practical steps to target doctor name searches with healthcare SEO. It also includes example guidance using Rust and the ggez game framework, without reusing other languages.

For a healthcare-focused approach, an healthcare SEO agency can help plan content, pages, and technical fixes. The steps below focus on how search intent works, and how pages can be built to match it.

What “doctor name searches” usually mean

Common search intent behind a doctor’s name

Search intent is what a person is trying to do when they type a query. With doctor name searches, the intent is often simple and direct. Many searches aim to confirm identity, find an appointment page, or check where the doctor practices.

Typical goals include finding the doctor’s profile, location, contact options, and patient resources. If the website does not clearly provide these details, searchers may bounce to another site.

Different query patterns that still count as doctor name searches

Doctor name searches do not always look the same. Some searches include middle initials, credentials, or location terms. Others add words like “appointment,” “specialist,” or “reviews.”

  • Full name: “Dr Jane Smith”
  • Name with credential: “John Doe MD” or “Jane Smith DO”
  • Name plus city: “Jane Smith cardiologist Austin”
  • Name plus service: “Dr Smith dermatology”
  • Name plus facility: “Dr Smith hospital”

How Google interprets name queries

Search engines try to match a person entity to the right page. The best chance comes from clear on-page signals. These include consistent spelling, a dedicated doctor profile page, and structured data that names the provider in a clear way.

When a site mixes multiple doctors on one page, it can become harder for search engines to connect the correct name to the correct profile.

Want To Grow Sales With SEO?

AtOnce is an SEO agency that can help companies get more leads and sales from Google. AtOnce can:

  • Understand the brand and business goals
  • Make a custom SEO strategy
  • Improve existing content and pages
  • Write new, on-brand articles
Get Free Consultation

Build a page map for each doctor profile

Create one dedicated page per doctor

Targeting a doctor name search is usually easier with a single, clear destination URL. A doctor profile page can match a searcher’s needs faster. It also supports consistent indexing.

A strong doctor profile page can include the following sections:

  • Doctor full name and credentials at the top
  • Specialty and service focus
  • Clinic locations and addresses
  • Phone number and scheduling actions
  • Education and training summary
  • Clinical interests and conditions treated
  • Languages spoken
  • Patient resources and next steps

Use consistent naming across the site

Inconsistent spelling can reduce match quality. Name fields in bios, navigation, and directory listings should match exactly. This can include middle initials and credential placement.

For example, if one profile uses “Dr. Aisha Rahman, MD” and another uses “Aisha Rahman MD,” those may be treated as different strings.

Plan internal links to doctor pages

Internal linking helps pages discover each other. It also helps search engines understand page relationships. Common linking places include:

  • Specialty landing pages that link to relevant doctors
  • Location pages that list doctors at that location
  • Author bylines on blog content
  • FAQ pages that mention specific provider expertise

For related work, see how to target treatment keywords in healthcare SEO. That guide focuses on service pages, which can connect strongly to doctor profile pages.

On-page SEO for doctor name targeting

Optimize the title and header structure

On-page SEO starts with clear headings. The doctor’s full name should appear in the page title and the main H2 or H1-equivalent heading. Many pages also include the credential and specialty in the first screen.

Example patterns that stay clear:

  • “Dr Jane Smith, MD | Cardiology in Austin, TX”
  • “Jane Smith, DO | Family Medicine | Locations and Appointments”

Write a short bio that matches real search phrases

A doctor bio should sound like a real summary. It should include specialties, common conditions treated, and clinical focus. This can help pages rank for name plus specialty queries.

Avoid vague text. If a doctor treats asthma and allergies, those topics should appear naturally. If a doctor does sports medicine, that term should appear as well.

Add location details that reflect how searches include cities

Many doctor name searches include a city or neighborhood. Location text on the profile page can help match those queries. Use the same city name spelling used in listings and contact pages.

Include:

  • Street address and suite number when allowed
  • City, state, and ZIP code
  • Hours of operation if available
  • Clear “Schedule” links for each location when there are multiple clinics

Use structured data for provider entities

Structured data can help search engines interpret page content. For doctor pages, provider-related schema can describe the person, specialty, and location details.

Common goals include:

  • Identifying the doctor name consistently
  • Linking specialties to the profile
  • Adding practice location info

Implementation details can vary by site setup, but the core idea stays the same: structured data should reflect what the page already states in plain text.

Doctor directory pages: helpful or harmful?

When directory pages support name searches

Directory pages can help if each doctor is clearly represented. A directory list should link to individual profile pages. The profiles then become the main targets for doctor name searches.

In a good directory, each entry should show:

  • Full name and credential
  • Specialty
  • Location label or nearest city
  • A direct link to the doctor profile

When directories fail

Directories can fail when they hide key details. If the list contains only names and no dedicated pages, search engines may not find enough content to match doctor intent. Also, if multiple doctors share similar names, missing details can cause confusion.

Another risk is when the directory uses infinite scroll without accessible links. Search engines may still crawl content, but clear anchor links usually make indexing and ranking more reliable.

Want A CMO To Improve Your Marketing?

AtOnce is a marketing agency that can help companies get more leads from Google and paid ads:

  • Create a custom marketing strategy
  • Improve landing pages and conversion rates
  • Help brands get more qualified leads and sales
Learn More About AtOnce

Off-page signals that support doctor name SEO

Make sure citations match the profile

Off-page signals often come from citations. These are mentions of a doctor and the practice in other places like directories and healthcare listing sites. The name should match the primary profile.

Consistency matters for spelling, credential, and location. When citations conflict, it can weaken the connection between the doctor’s entity and the correct page.

Get reviews and mentions in a controlled way

Reputation content can influence click behavior. It can also support the perception that the profile is real and active.

When reviews are used, they should not be used to replace factual profile content like scheduling and locations. The profile should remain the main source of truth.

If content overlap becomes a problem, it can help to review how pages consolidate. For related guidance, see how to consolidate overlapping healthcare content.

Technical SEO steps that support doctor name pages

Make doctor pages fast and crawlable

Technical SEO affects how quickly pages load and how easily bots read them. Doctor profiles should be lightweight. They should render quickly and avoid blocking scripts that hide names and specialties.

Key areas include:

  • Readable HTML content for names and headings
  • Stable URL structure per doctor
  • Clean internal linking from category pages
  • No accidental redirects between duplicate URLs

Handle duplicates and “near-duplicate” profiles

Duplicate pages can happen when the same doctor has multiple locations but uses separate URLs, or when old pages are not retired properly. When duplicates exist, search engines may choose the wrong one.

Consolidation helps in many cases. If multiple URLs represent the same doctor identity and content, a plan can reduce confusion.

Use redirects carefully during migrations

During site changes, doctor name pages can lose rankings if redirects are not mapped correctly. A migration plan should include every doctor URL and its destination.

For a workflow that many teams use, see healthcare SEO migration planning.

Measurement: how to tell if doctor name SEO is working

Track branded queries and profile clicks

Doctor name searches are often branded, meaning the query includes the provider’s name. Analytics can show clicks to doctor pages from search results.

A practical approach includes:

  • Monitoring impressions and clicks for doctor profile URLs
  • Checking which query patterns appear with the name (city, specialty, appointment)
  • Reviewing click-through rate changes after page updates

Look at on-page behavior

After landing on a doctor profile, visitors may seek scheduling or contact. Measuring scroll depth or engagement can help identify missing sections. For example, if visitors leave quickly, location details or appointment actions may be hard to find.

Run focused content updates

Updates should be narrow and safe. If the goal is to improve doctor name plus city ranking, improvements should include consistent location text. If the goal is doctor name plus specialty, the bio can add specialty phrases that match the services offered.

Want A Consultant To Improve Your Website?

AtOnce is a marketing agency that can improve landing pages and conversion rates for companies. AtOnce can:

  • Do a comprehensive website audit
  • Find ways to improve lead generation
  • Make a custom marketing strategy
  • Improve Websites, SEO, and Paid Ads
Book Free Call

Implementation example: using Rust and ggez to support SEO workflows (no language reimplementation)

Why include a code example in an SEO article

Some teams want small internal tools to support SEO work, like generating XML sitemaps, checking page status, or creating content QA screens. If those tools use Rust, it may be preferred to keep the same language across the workflow.

The example below shows how to build a small ggez app in Rust that can help visualize a simple “doctor page checklist” state machine. It does not replace SEO work, but it shows a way to build internal UI with the same language and framework.

Rust with ggez: keep the same language and framework

The requirement here is to use Rust with ggez, and not reimplement the logic in another language. The code uses the ggez event loop and draws UI state.

In a real internal tool, “checklist items” could map to SEO tasks like title checks, structured data presence, and location text verification. The UI could show states for each doctor profile.

Example ggez state machine using the same Rust codebase pattern

The code below follows the EventHandler pattern and updates UI state over time. It uses the same language and library, and it does not switch to another language for helper logic.

use ggez::event::{self, EventHandler};
use ggez::graphics::{self, Color, DrawMode, DrawParam, Mesh, Rect, Text};
use ggez::input::keyboard::{self, KeyCode};
use ggez::{Context, ContextBuilder, GameResult};

const SCREEN_W: f32 = 900.0;
const SCREEN_H: f32 = 600.0;

const PANEL_W: f32 = 520.0;
const PANEL_H: f32 = 430.0;

const ITEM_W: f32 = 420.0;
const ITEM_H: f32 = 44.0;

const BG: Color = Color::from_rgb(20, 20, 20);
const WHITE: Color = Color::WHITE;

struct ChecklistItem {
    label: String,
    done: bool,
}

struct AppState {
    items: Vec<ChecklistItem>,
    selected: usize,
    game_over: bool,
}

impl AppState {
    fn new() -> Self {
        Self {
            items: vec![
                ChecklistItem { label: "Doctor name matches profile title", done: false },
                ChecklistItem { label: "Dedicated doctor profile URL", done: false },
                ChecklistItem { label: "Location text includes city and address", done: false },
                ChecklistItem { label: "Structured data includes provider entity", done: false },
                ChecklistItem { label: "Internal links point to this profile", done: false },
            ],
            selected: 0,
            game_over: false,
        }
    }

    fn toggle_selected(&mut self) {
        if let Some(item) = self.items.get_mut(self.selected) {
            item.done = !item.done;
        }
    }
}

impl EventHandler for AppState {
    fn update(&mut self, ctx: &mut Context) -> GameResult {
        if keyboard::is_key_pressed(ctx, KeyCode::Escape) {
            *&mut self.game_over = true;
        }

        if keyboard::is_key_pressed(ctx, KeyCode::Up) {
            if self.selected > 0 {
                self.selected -= 1;
            }
        }

        if keyboard::is_key_pressed(ctx, KeyCode::Down) {
            if self.selected + 1 < self.items.len() {
                self.selected += 1;
            }
        }

        if keyboard::is_key_pressed(ctx, KeyCode::Space) {
            self.toggle_selected();
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, BG);

        let panel_x = (SCREEN_W - PANEL_W) / 2.0;
        let panel_y = (SCREEN_H - PANEL_H) / 2.0;

        let panel_mesh = Mesh::new_rectangle(
            ctx,
            DrawMode::fill(),
            Rect::new(panel_x, panel_y, PANEL_W, PANEL_H),
            Color::from_rgb(35, 35, 35),
        )?;
        graphics::draw(ctx, &panel_mesh, DrawParam::default())?;

        let header = Text::new("Doctor Name SEO Checklist (UI Demo)");
        graphics::draw(
            ctx,
            &header,
            DrawParam::default().dest([panel_x + 20.0, panel_y + 18.0]),
        )?;

        for (i, item) in self.items.iter().enumerate() {
            let row_y = panel_y + 70.0 + i as f32 * 55.0;

            let is_selected = i == self.selected;
            let item_color = if item.done {
                Color::from_rgb(70, 120, 70)
            } else if is_selected {
                Color::from_rgb(90, 90, 150)
            } else {
                Color::from_rgb(55, 55, 55)
            };

            let row_mesh = Mesh::new_rectangle(
                ctx,
                DrawMode::fill(),
                Rect::new(panel_x + 20.0, row_y, ITEM_W, ITEM_H),
                item_color,
            )?;

            graphics::draw(ctx, &row_mesh, DrawParam::default())?;

            let status = if item.done { "[X] " } else { "[ ] " };
            let line = Text::new(format!("{}{}", status, item.label));

            graphics::draw(
                ctx,
                &line,
                DrawParam::default().dest([panel_x + 28.0, row_y + 10.0]),
            )?;
        }

        let help = Text::new("Use Up/Down to select. Space to toggle. Esc to quit.");
        graphics::draw(
            ctx,
            &help,
            DrawParam::default().dest([panel_x + 20.0, panel_y + PANEL_H - 30.0]),
        )?;

        graphics::present(ctx)?;
        Ok(())
    }
}

fn main() -> GameResult {
    let (ctx, event_loop) = ContextBuilder::new("doctor_seo_checklist", "ggez")
        .window_setup(ggez::conf::WindowSetup::default().title("Doctor SEO Checklist"))
        .window_mode(
            ggez::conf::WindowMode::default()
                .dimensions(SCREEN_W, SCREEN_H)
                .resizable(false),
        )
        .build()?;

    let state = AppState::new();
    event::run(ctx, event_loop, state)
}

How this UI can map to real SEO tasks

This checklist approach can reflect real work tied to doctor name searches. Each row can correspond to an audit rule. For example, “Dedicated doctor profile URL” can be verified by confirming one unique page for each provider.

“Location text includes city and address” can be checked by comparing profile content to known practice details. “Internal links point to this profile” can be checked by crawling and searching for links.

Common mistakes when targeting doctor name searches

Using one generic page for many doctors

A single specialty page can rank for broad queries. It can also support discovery. But doctor name searches usually need a dedicated page where the correct person is clearly stated.

Leaving out the location that people search

When searches include a city, profile pages should include that city in plain text. Location details should not be only in images, hidden tabs, or files that search engines do not read well.

Changing URLs without redirects

SEO rankings can drop after reorganizations. If doctor profile URLs change, redirects and mappings should be planned first. This is especially important for name queries because users may link to older URLs.

Practical rollout plan for doctor name SEO

Step 1: Confirm the target pages

List the doctors and choose the main URL for each doctor profile. Avoid multiple competing URLs for the same identity. Then confirm the spelling and credentials on the page.

Step 2: Improve on-page elements in small updates

Update titles, headings, and the first screen of each profile. Add location text in plain HTML. Add clear appointment or contact actions.

Step 3: Add structured data and verify it

Implement structured data that matches the content shown on the page. Then validate it with common structured-data tools. Fix warnings that relate to missing or conflicting fields.

Step 4: Strengthen internal links

Link from specialty and location pages into each doctor profile. Use consistent anchor text that includes the doctor name when it fits naturally.

Step 5: Track branded queries and adjust

Monitor doctor name query performance for each profile. If a profile ranks for name only but not for name plus city, focus on location clarity. If it ranks for name but not for name plus specialty, strengthen the specialty and clinical focus sections.

Conclusion

Doctor name searches work best when each doctor has a clear profile page with consistent naming, helpful location details, and strong internal linking. On-page SEO, structured data, and technical crawl access can support better matching for name queries. With careful measurement, updates can be focused on the exact query patterns that drive visits.

For planning support, healthcare SEO services and migration workflows can reduce risk. When content overlap occurs, consolidation can also help. The goal is simple: make the doctor profile the most direct match for the doctor name search intent.

Want AtOnce To Improve Your Marketing?

AtOnce can help companies improve lead generation, SEO, and PPC. We can improve landing pages, conversion rates, and SEO traffic to websites.

  • Create a custom marketing plan
  • Understand brand, industry, and goals
  • Find keywords, research, and write content
  • Improve rankings and get more sales
Get Free Consultation