Tutorial
Aug 6, 202657 views

How to Get a Google Places API Key (Step-by-Step Guide)

A complete walkthrough for creating a Google Places API key in 2026 — Cloud project setup, billing, enabling Places API (New), key restrictions, a working test request, and the pricing model that catches teams out.

GooglePlaceAPIGoogle Places APIGoogle CloudAPI Keys
VS
Vikash SinghUpdated Jan 5, 2026
Likes0
Shares0
57 views · 57 YouTube viewsAug 6, 2026

TL;DR

Create a Google Cloud project, enable billing, enable Places API (New), then create a key under Credentials. Restrict it immediately by referrer or IP and by API. Every request needs an X-Goog-FieldMask header, and the fields you request decide your price tier.

Walkthrough

Step-by-step.

You need a Google Places API key to pull address autocomplete, business listings, or location search into your app. Getting one takes about ten minutes.

The setup itself is straightforward. What trips people up is what comes after: billing, key restrictions, and a pricing model that changed significantly in 2025. This guide covers all of it.

What the Places API Actually Does

The Places API gives your application access to Google's database of over 200 million places. Businesses, landmarks, addresses, points of interest.

Most teams use it for one of four things. Address autocomplete in a checkout or signup form. A store locator. Search for nearby businesses. Pulling details like opening hours, phone numbers, or ratings into a listing.

One important thing before you start. There are two versions: Places API and Places API (New). Since March 2025, the legacy version can no longer be enabled on new Google Cloud projects. If you are starting fresh, Places API (New) is your only option. Projects that already had legacy access can keep using it, but it is frozen and receives no new features.

Build on the new one.

Before You Start

You need three things.

A Google account. Any Gmail account works.

A Google Cloud project. We create one below.

A billing account with a valid card. This is required even if you plan to stay inside the free usage limits. Google will not issue a working key without it.

Step 1: Create or Select a Google Cloud Project

Go to console.cloud.google.com and sign in.

Click the project dropdown at the top of the page. Then click New Project.

Give it a name that identifies the app, not something generic. "MyProject" becomes useless once you have five of them.

Click Create and wait a few seconds. Then make sure the new project is selected in the dropdown before continuing. Enabling an API on the wrong project is the single most common mistake in this whole process.

Step 2: Enable Billing

In the left sidebar, open Billing.

Link a billing account, or create one if this is your first project. You will enter card details.

Google does not charge you for staying inside the free monthly limits. But without a billing account attached, every API call returns a permission error, and the error message does not tell you billing is the reason.

Step 3: Enable the Places API (New)

In the left sidebar, go to APIs and Services, then Library.

Search for "Places API (New)". Note the "(New)" — you may also see the legacy entry listed.

Click it, then click Enable.

If you are building address autocomplete on a web page, also enable Maps JavaScript API. If you need to convert addresses into coordinates, enable Geocoding API too. Each one is enabled separately.

Step 4: Create Your API Key

Go to APIs and Services, then Credentials.

Click Create Credentials at the top, then choose API key.

A dialog appears with your new key. Copy it somewhere safe.

That key now works. It is also completely unrestricted, which means anyone who finds it can run up charges on your billing account. Do not stop here.

Step 5: Restrict the Key

This step is not optional and it is the step most tutorials skip.

Click Edit API key on the key you just created. You will set two kinds of restriction.

Application restrictions control who can use the key.

For a website, choose HTTP referrers and add your domains: https://yourdomain.com/* and https://*.yourdomain.com/*. Add http://localhost:3000/* for local development.

For a server-side backend, choose IP addresses and add your server's IP.

For mobile, choose Android apps or iOS apps and supply the package name with SHA-1 fingerprint, or the bundle ID.

API restrictions control what the key can call.

Select Restrict key, then tick only the APIs you enabled in Step 3. A key that can call every Google API is a liability.

Save. Restrictions can take up to five minutes to take effect.

Step 6: Test the Key

Run this from your terminal, replacing YOUR_API_KEY:

curl -X POST -d '{"textQuery":"coffee in Jaipur"}' -H "Content-Type: application/json" -H "X-Goog-Api-Key: YOUR_API_KEY" -H "X-Goog-FieldMask: places.displayName,places.formattedAddress" https://places.googleapis.com/v1/places:searchText

You should get JSON back with place names and addresses.

Note the X-Goog-FieldMask header. The new Places API rejects requests without it. This catches out everyone migrating from the legacy API, where the header did not exist.

Understanding Places API Pricing

Google restructured Maps Platform billing on 1 March 2025. The old flat $200 monthly credit is gone. It was replaced by free monthly usage caps applied per SKU, organised into Essentials, Pro, and Enterprise tiers.

Here is the part that costs teams real money: your SKU tier is determined by which fields you request, not which endpoint you call.

Ask for place IDs and names only, and you sit in the cheapest tier. Add formatted address and location, and you move up. Add phone number or website, and you move up again. Add ratings or reviews, and you land in the most expensive tier.

Requesting every available field "just in case" can multiply your bill several times over. Set a tight field mask and only widen it when you actually need more.

Two more things worth knowing. Use session tokens for autocomplete, which bundles the keystrokes into one billable session instead of charging per character. And set hard per-API quotas in the console — budget alerts only notify you, they do not stop usage.

Rates and free caps change. Confirm the current numbers on your own Cloud Console billing page before you commit to an architecture.

Common Errors and What They Mean

REQUEST_DENIED, "not authorized to use this API." The API is not enabled on this project, or you are on the wrong project.

403 PERMISSION_DENIED. Usually billing. Check that a billing account is linked and active.

"API keys with referer restrictions cannot be used with this API." You put an HTTP referrer restriction on a key being used server-side. Referrer restrictions only work for browser requests. Create a second key with IP restrictions for your backend.

400, field mask required. You omitted the X-Goog-FieldMask header. Required on every Places API (New) request.

RefererNotAllowedMapError. Your current domain is not in the referrer list. Check for a missing wildcard or a forgotten localhost entry.

Everything worked, then stopped after five minutes. Restrictions finished propagating and one of them is wrong.

Keeping the Key Secure in Production

Never commit an API key to Git. Use environment variables and add .env to .gitignore.

Use separate keys for development, staging, and production. If one leaks, you rotate one key instead of taking down everything.

Browser keys are visible in your page source. That is unavoidable — which is exactly why the referrer restriction matters. For anything sensitive, proxy the call through your own backend so the key never reaches the client.

Set a billing budget alert. Then set per-API quotas as well, because alerts alone will not stop a runaway loop.

Where This Fits in a Real Project

An API key is the starting line, not the finish. The work after this is rate limiting, caching responses so you are not paying twice for the same lookup, and handling the failure cases gracefully when Google returns nothing.

Our team has built location-aware features into production apps across logistics, events, and food delivery. You can see the stack we work with on our technologies page and browse shipped work in our projects portfolio.

If you are setting up the surrounding environment, the NVM on Windows tutorial covers Node version management, and the SSL certificate guide handles securing the server this will run on.

More walkthroughs are on the tutorials hub, and longer technical writing lives on the blog.

Answers

Frequently asked.

Is the Google Places API free?+

There is no longer a flat $200 monthly credit. Since March 2025, Google applies free monthly usage caps per SKU. Light usage such as a small store locator often stays free, but production traffic exceeds the caps. A billing account with a valid card is required regardless.

What is the difference between Places API and Places API (New)?+

Places API (New) is the current version with a different request format, mandatory field masks, and its own SKU pricing. The legacy version was frozen in March 2025 and can no longer be enabled on new Cloud projects. New builds must use Places API (New).

Why does my Places API key return REQUEST_DENIED?+

Three usual causes. The API is not enabled on the project you are calling from. Billing is not linked. Or a key restriction is blocking the request — for example a referrer restriction on a key being used from a server.

Do I need a credit card for a Google Places API key?+

Yes. Google requires an active billing account before the key will return data, even if your usage stays inside the free monthly caps.

Can I use one API key for multiple Google APIs?+

Technically yes, but you should not. Restrict each key to only the APIs it needs, and use separate keys for development, staging, and production. A leaked unrestricted key can be used against any enabled service on your billing account.

How do I stop my Google Places API bill from getting out of control?+

Request the minimum set of fields in your field mask, since fields determine your price tier. Use session tokens for autocomplete. Cache repeated lookups. And set per-API quotas in the Cloud Console — budget alerts notify you but do not stop usage.

Craxinno Service

Have a project in mind?

Our team has shipped production software for client apps — from initial integration to scaling, caching and cost optimisation. We’d love to help.

Was this tutorial helpful?Your feedback helps us prioritise what to publish next.

Continue with Tutorials.

View all tutorials
How to Send Twilio SMS Using Your Brand Name (Sender ID Setup)
Twilio

How to Send Twilio SMS Using Your Brand Name (Sender ID Setup)

Getting an SMS from "CRAXINNO" instead of a random number looks more legitimate and gets opened more often. Twilio calls this an Alphanumeric Sender ID, and in supported countries it takes about ten minutes to set up. The catch is the phrase "in supported countries." Where you are sending decides almost everything about how this works, and one very large market does not support it at all. Read This First: Where Alphanumeric Sender IDs Do Not Work Alphanumeric Sender IDs are not available for the United States or Canada. Not with an application, not with a business account, not with a workaround. The carriers do not accept them. For US traffic, you register a phone number through A2P 10DLC, or use a toll-free number or short code. Your brand identity comes from carrier-level brand registration rather than from the sender string. This catches out a lot of teams who build the integration first and discover the restriction when messages start failing. If your primary market is North America, stop here and read Twilio's A2P 10DLC documentation instead. Where it does work: most of Europe, the UK, Australia, India, much of Asia, Africa, and the Middle East. Country support and requirements change regularly, so check Twilio's country guidelines for your specific destinations before you build. Three Levels of Country Requirement Supported countries fall into three groups, and knowing which one you are sending to determines your timeline. No registration needed. Set the sender string and send. The UK, Australia, and most of Western Europe work this way. This is what the video demonstrates. Pre-registration required. You submit the sender ID to Twilio, who registers it with local carriers. Approval takes days to weeks. India, Vietnam, the Philippines, Saudi Arabia, the UAE, and Qatar all sit here. Sending an unregistered ID to these countries means silent failure or substitution with a random number. Not supported. The US and Canada, as covered above. India deserves a specific mention because it is the strictest. You need DLT registration with an Indian telecom operator, covering your entity, your sender ID header, and every message template you intend to send. Unregistered templates are blocked. Budget two to three weeks. The Limitation Nobody Mentions Until It Bites Alphanumeric Sender IDs are one-way only. Recipients cannot reply. There is no inbound path. If someone tries, the message goes nowhere and they get no error. This makes them right for one-time passcodes, delivery notifications, appointment reminders, and transactional alerts. It makes them wrong for anything conversational — support threads, two-way confirmations, or any flow where a customer might reasonably respond. If you need replies, use a phone number. Some teams run both: a sender ID for outbound notifications and a number for conversations. Sender ID Format Rules Between 1 and 11 characters. Letters A-Z and a-z, digits 0-9, and spaces. At least one letter is required — an all-numeric string is not valid. No leading or trailing spaces. Two practical notes. Casing is not guaranteed to survive; some carriers normalise everything to uppercase. And avoid characters that look alike across fonts, since a sender ID exists to be recognised at a glance. Pick something short and obviously yours. "CRAXINNO" reads better than "CraxinnoTech" truncated to eleven characters. Step 1: Get Your Credentials Sign in to the Twilio Console. Your Account SID and Auth Token are on the dashboard. The Auth Token is a password. Do not commit it, do not paste it into a client-side file, and do not put it in a support ticket. Store both in environment variables. For anything beyond a test, use an API Key and Secret instead of the Auth Token directly. API keys can be revoked individually without rotating your account credentials, which matters when someone leaves or a laptop goes missing. Step 2: Install the SDK npm install twilio Then create a .env file: 1 2 3 TWILIO_ACCOUNT_SID = your_account_sid TWILIO_AUTH_TOKEN = your_auth_token TWILIO_SENDER_ID = CRAXINNO Add .env to .gitignore before you write anything into it. Step 3: Send the Message 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 require ( 'dotenv' ) . config ( ) ; const twilio = require ( 'twilio' ) ; const client = twilio ( process . env . TWILIO_ACCOUNT_SID , process . env . TWILIO_AUTH_TOKEN ) ; async function sendSMS ( to , body ) { try { const message = await client . messages . create ( { body , from : process . env . TWILIO_SENDER_ID , to } ) ; console . log ( 'Sent:' , message . sid ) ; return message ; } catch ( error ) { console . error ( 'Failed:' , error . code , error . message ) ; throw error ; } } sendSMS ( '+447700900000' , 'Your verification code is 482913' ) ; The only difference from sending via a phone number is the from value. Instead of a number in E.164 format, you pass your sender string. The to number must be in E.164 format — a plus sign, country code, then the number, with no spaces or dashes. Step 4: Verify Delivery A successful API call means Twilio accepted the message, not that it arrived. Check the actual status: 1 2 const sent = await client . messages ( message . sid ) . fetch ( ) ; console . log ( sent . status , sent . errorCode ) ; Status moves through queued , sent , and ideally delivered . A status of undelivered or failed comes with an error code that tells you why. For anything in production, configure a status callback webhook rather than polling. Twilio posts delivery updates to your endpoint as they happen. Using a Messaging Service Instead For production traffic, create a Messaging Service in the Twilio Console rather than hardcoding the sender. Add both your alphanumeric sender ID and a phone number to the sender pool. Twilio then picks the right sender for each destination automatically — the sender ID where it is supported, the number where it is not. In code you pass messagingServiceSid instead of from . That one change means a message to a US number does not simply fail; it falls back to your registered number. This is the difference between a demo and something you can run a business on. Common Errors 21612 — cannot route to this number. Usually an unsupported destination country for alphanumeric senders. This is what a US number returns. 21606 — the From number is not a valid, SMS-capable inbound number. Your sender string breaks a format rule, or the destination country requires pre-registration you have not completed. Message shows as delivered but the sender is a random number. The carrier substituted it. Common where registration is required and has not been done. 30007 — message filtered. The carrier blocked it, typically for content resembling spam or, in India, an unregistered template. Works in testing, fails in one country. Almost always a country-specific registration requirement. Check Twilio's country guidelines for that destination. 21608 — unverified number on a trial account. Trial accounts can only send to numbers you have verified in the console. Upgrade or verify the recipient. Before You Go Live Test with a real handset in each destination country. Emulators and virtual numbers do not reflect carrier behaviour. Keep a fallback sender configured. A Messaging Service handles this; hardcoded from values do not. Log the message SID for every send. When a customer says they never received a code, the SID is the only way to find out what actually happened. Respect local rules on timing and consent. Several jurisdictions restrict marketing SMS to certain hours and require documented opt-in. The penalties are real. Monitor your delivery rate per country. A drop in one market usually means a carrier policy change rather than a bug in your code. Where This Fits in a Real Project SMS is rarely the whole feature. It sits inside authentication, order flows, or a notification system, alongside retry logic, rate limiting, and a record of what was sent to whom. Our team builds and ships production integrations like this for client software — messaging, payments, and third-party APIs, with the operational layer around them. You can see the stack we work across on our technologies page and browse shipped work in our projects portfolio. If you are setting up the environment around this, the NVM on Windows tutorial covers Node version management, and the SSL certificate guide handles securing the server your webhook endpoint runs on. More walkthroughs are on the tutorials hub, and longer technical writing lives on the blog .

Posted 28.11.2025
How to Deploy HTML and CSS Projects on Netlify (Manual Upload)
HTML

How to Deploy HTML and CSS Projects on Netlify (Manual Upload)

You built something with HTML and CSS. Now you want a link you can send to someone. Netlify's drag-and-drop deploy gets you a live URL in under a minute. No Git, no terminal, no build tools. Drag a folder onto a page and the site is online. This guide covers the upload, plus the three things that go wrong immediately afterward: broken image paths, subpages returning 404, and a custom domain that will not connect. What You Need A folder containing your project files. HTML, CSS, images, whatever else the site uses. A file named index.html at the top level of that folder. Not home.html . Not Index.html . Netlify looks for exactly index.html , lowercase, and serves it as your homepage. Getting this wrong is the most common reason a fresh deploy shows a blank page or a directory listing. A Netlify account. The free tier is generous and no card is required. This works for vanilla CSS, Bootstrap, and Tailwind — as long as Tailwind is loaded via CDN or you have already compiled your CSS to a plain file. If you are running a Tailwind build step, compile first and upload the output. Step 1: Check Your Folder Structure Before uploading, open the folder and confirm the layout looks like this: 1 2 3 4 5 6 7 8 my - project / ├── index . html ├── css / │ └── style . css ├── js / │ └── script . js └── images / └── logo . png The critical detail is that index.html sits at the root of the folder you drag, not inside a subfolder. If your files are nested one level deeper, drag the inner folder instead. Step 2: Deploy Sign in at app.netlify.com. On your team dashboard, find the drag-and-drop area — it usually reads "Drag and drop your site output folder here" or sits under Add new site, then Deploy manually. Drag your project folder onto it. Upload takes a few seconds. Netlify assigns a random subdomain such as spontaneous-halva-4f2a91.netlify.app . Your site is live at that URL. That is the whole deploy. Step 3: Rename the Site The random name is fine for a quick share and terrible for a portfolio. Go to Site configuration, then Site details, then Change site name. Pick something readable — yourname-portfolio.netlify.app . Do this before you share the link anywhere, because the old URL stops working once you change it. Fixing Broken Images and Missing CSS Your site loads but the styling is gone and images are missing. This happens to almost everyone on their first deploy, and there are two causes. Case sensitivity. Windows and macOS treat Logo.png and logo.png as the same file. Netlify's servers do not. If your HTML references images/Logo.png and the actual file is logo.png , it worked locally and it will 404 in production. Fix: make every filename lowercase, and make every reference in your HTML match exactly. Absolute local paths. If your HTML contains something like C:/Users/You/Desktop/project/css/style.css , or a leading slash pointing somewhere that does not exist on the server, the browser cannot find the file. Fix: use relative paths. css/style.css and images/logo.png , referenced from where index.html sits. Open your browser's developer console on the live site. Every failed file shows up there as a 404 with the exact path it tried, which usually tells you the answer immediately. Fixing 404s on Subpages Your homepage works, but yoursite.netlify.app/about returns a 404. For a static HTML site, Netlify serves files by path. /about only works if a file named about.html exists — Netlify will match the clean URL to it. If you have pages/about.html , then the URL is /pages/about . If your site is a single-page app using client-side routing, you need a _redirects file at the root of the folder you upload, containing: 1 /* /index.html 200 That tells Netlify to serve index.html for every path and let your JavaScript handle routing. For a plain HTML and CSS site, you do not need this. Adding a Custom Domain Go to Domain management, then Add a domain, and enter your domain. You have two paths. Netlify DNS. Netlify gives you four nameservers to enter at your domain registrar. This is the simpler option and it handles the SSL certificate automatically. External DNS. Keep your current DNS provider and add records manually. Point the apex domain at Netlify's load balancer IP, and add a CNAME for www pointing to your Netlify subdomain. Netlify shows you the exact values. Either way, DNS propagation takes anywhere from a few minutes to 48 hours. Netlify provisions a free Let's Encrypt certificate automatically once the domain resolves, so leave it a while before assuming something is broken. If HTTPS does not appear after a day, check Domain management for a certificate error. It is nearly always a DNS record that has not propagated or a conflicting record left over from a previous host. Updating a Manually Deployed Site Here is the tradeoff nobody mentions in the drag-and-drop tutorials. Every change means dragging the entire folder again. There is no version history you can meaningfully work with, no rollback to a specific commit, and no record of what changed between deploys. For a one-off share or a class assignment, that is fine. For anything you will update more than a handful of times, connect a Git repository instead. Push to your branch and Netlify rebuilds automatically. You get deploy previews on pull requests, a full history, and one-click rollback to any previous deploy. The manual upload is the right way to start. It is the wrong way to maintain something. When to Move Beyond Manual Deploys Move to a Git-connected deploy when any of these become true. You are updating the site regularly. Dragging a folder gets old fast. You need a build step. Tailwind compilation, Sass, a bundler, any framework — Netlify runs the build for you on a Git deploy. More than one person touches the site. Manual uploads have no merge story at all. You need environment variables or serverless functions. Connecting a repo takes about two minutes: Add new site, then Import an existing project, then authorise GitHub and pick the repository. Netlify's Free Tier Limits Worth knowing before you build something that outgrows it. The free tier covers a substantial amount of bandwidth and build minutes per month, and supports custom domains with free SSL. Concurrent builds are limited to one, and some team features are paid. The specific numbers change. Check Netlify's current pricing page rather than trusting any figure in a tutorial, including this one. Where This Fits in a Real Project Static hosting handles the frontend. The moment you need authentication, a database, payments, or an API, the architecture question changes — and that decision is easier to make early than to retrofit. Our team builds and ships production frontends in React and Next.js, along with the backend infrastructure that supports them. You can see the stack we work across on our technologies page and browse shipped work in our projects portfolio . If you are setting up a local environment, the NVM on Windows tutorial covers Node version management. For self-hosted deployments, the SSL certificate guide handles TLS on Nginx. More walkthroughs are on the tutorials hub, and longer technical writing lives on the blog .

Posted 28.11.2025
How to Create and Run a React Project with TypeScript
React

How to Create and Run a React Project with TypeScript

Setting up a React project with TypeScript takes about two minutes with the right tool. The hard part is knowing which tool to use, because the answer changed. Create React App was the default for years. It was deprecated in February 2025 and is no longer recommended for new projects. If a tutorial tells you to run npx create-react-app , it is out of date. This guide uses Vite, which is what the React team now points people toward for single-page apps. What You Need First Node.js version 20 or higher. Check with: node -v If that command is not recognised or the number is below 20, install Node first. On Windows, the NVM tutorial covers installing it in a way that lets you switch versions per project. You also need npm, which ships with Node. Confirm with npm -v . A code editor. VS Code has the strongest TypeScript support out of the box. Why Vite Instead of Create React App Three reasons, briefly. Speed. Vite starts a dev server in under a second because it serves native ES modules rather than bundling everything upfront. CRA's Webpack setup takes tens of seconds on a large project. Maintenance. CRA has not received meaningful updates in years and its dependency tree carries known vulnerabilities that cannot be resolved without ejecting. Direction. The React documentation now recommends either a framework such as Next.js, or Vite for a plain single-page app. If you are building something with routing, data fetching, and SEO requirements, consider Next.js instead. Vite is the right answer for a client-side app, a dashboard behind a login, or anything you are learning on. Step 1: Create the Project Run this in the folder where you keep your projects: npm create vite@latest my-app -- --template react-ts The react-ts template is the important part. It scaffolds React with TypeScript already configured — no separate setup step. If you prefer the interactive prompts, run npm create vite@latest on its own and select React, then TypeScript, when asked. Step 2: Install Dependencies cd my-app npm install This pulls down React, TypeScript, Vite, and the type definitions. It takes a few seconds. Step 3: Run It npm run dev Vite prints a local URL, usually http://localhost:5173 . Open it and you have a working React app with hot module replacement — save a file and the browser updates without a full reload, preserving component state. Note the command. Vite uses npm run dev , not npm start . That difference catches out everyone coming from CRA. Understanding the Project Structure 1 2 3 4 5 6 7 8 9 10 11 my - app / ├── public / ├── src / │ ├── App . tsx │ ├── main . tsx │ ├── index . css │ └── vite - env . d . ts ├── index . html ├── package . json ├── tsconfig . json └── vite . config . ts A few things differ from what you may expect. index.html sits at the project root, not inside public/ . Vite treats it as the entry point and processes it directly. main.tsx is where React mounts to the DOM. App.tsx is your root component. .tsx is the extension for files containing JSX. Plain TypeScript files without JSX use .ts . vite-env.d.ts gives TypeScript the type definitions for Vite's environment variables. Leave it alone. Writing Your First Typed Component Delete the contents of App.tsx and replace with: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type GreetingProps = { name : string ; count ? : number ; } ; function Greeting ( { name , count = 0 } : GreetingProps ) { return ( < div > < h1 > Hello , { name } < / h1 > < p > Clicked { count } times < / p > < / div > ) ; } export default function App ( ) { return < Greeting name = "Nimish" / > ; } Two things worth understanding here. The type block defines the shape of the props. TypeScript now checks every use of Greeting against it. Pass a number where a string is expected and your editor flags it before you run anything. The ? on count marks it optional. Without the ? , TypeScript requires it on every usage. Try removing name="Nimish" and watch the error appear in your editor. That immediate feedback is the entire value proposition of TypeScript. Typing State and Events Two patterns you will use constantly. State usually infers its own type: 1 const [ count , setCount ] = useState ( 0 ) ; TypeScript works out that count is a number from the initial value. You only need an explicit type when the initial value does not tell the full story: 1 const [ user , setUser ] = useState < User | null > ( null ) ; Event handlers need their event typed: 1 2 3 function handleChange ( e : React . ChangeEvent < HTMLInputElement > ) { setName ( e . target . value ) ; } The element type inside the angle brackets matters. HTMLInputElement for inputs, HTMLTextAreaElement for textareas, HTMLSelectElement for selects. Getting it wrong means e.target.value will not be typed correctly. Building for Production npm run build This type-checks the whole project, then compiles and bundles into a dist folder. The build fails on type errors. That is intentional and it is the point — a type error caught at build time is a bug that never reaches a user. Preview the production build locally before deploying: npm run preview The dist folder is what you deploy. It is static output, so it works on Netlify, Vercel, S3, or any static host. The Netlify deployment guide covers that process, though for a Vite app you would normally connect Git rather than dragging the folder. Common Errors and Fixes Cannot find module './App' or its corresponding type declarations — usually a case mismatch in the filename, or a missing .tsx extension on the file itself. JSX element implicitly has type 'any' — the React type definitions are missing. Run npm install -D @types/react @types/react-dom . Property does not exist on type — you are accessing a field that is not in your type definition. Add it to the type rather than reaching for any . Port 5173 already in use — another Vite server is running. Either stop it, or start this one with npm run dev -- --port 3000 . Blank page after building and deploying — the base path is wrong. If you are deploying to a subdirectory, set base: '/subdirectory/' in vite.config.ts . Environment variables are undefined — Vite only exposes variables prefixed with VITE_ . Rename API_URL to VITE_API_URL and access it via import.meta.env.VITE_API_URL , not process.env . Migrating an Existing CRA Project If you have a Create React App project you want to move over, the outline is: Install Vite and the React plugin, remove react-scripts , move index.html from public/ to the root and add a script tag pointing at src/main.tsx , rename any .js files containing JSX to .jsx or .tsx , swap process.env.REACT_APP_* for import.meta.env.VITE_* , and update your package scripts to dev , build , and preview . Budget an afternoon for a mid-sized app. Most of the time goes on environment variables and any Webpack-specific configuration you had customised. Where This Fits in a Real Project A scaffolded project is the starting point. What follows is routing, state management, a testing setup, and a CI pipeline — and those decisions are cheaper to make now than to retrofit later. Our team builds production React and Next.js applications in TypeScript for client software. You can see the stack we work across on our technologies page and browse shipped work in our projects portfolio . If you are still setting up locally, the NVM on Windows tutorial handles Node version management. For deployment, the Netlify guide covers static hosting and the SSL certificate guide covers self-hosted TLS. More walkthroughs are on the tutorials hub, and longer technical writing lives on the blog .

Posted 02.12.2025
Connect With Us

Have something in mind?

We take on a handful of new custom-software engagements every quarter. If your problem is interesting and your timeline is real — let’s talk.

Let’s ConnectAvg. response · under 4 hours
01
Ideate · 1 weekWorkshops, scoping, success metrics agreed.
02
Design + Build · 8–14 weeksBi-weekly demos. Production code from week one.
03
Ship + Support · ongoingDeployment, observability, and a long-tail retainer.